diff --git a/examples/Advanced/Display/HZK16/HZK16.ino b/examples/Advanced/Display/HZK16/HZK16.ino index 709815a..8c40077 100644 --- a/examples/Advanced/Display/HZK16/HZK16.ino +++ b/examples/Advanced/Display/HZK16/HZK16.ino @@ -1,50 +1,45 @@ -/* - 在 src.h 文件中 GbkStr 显示为乱码为正常现象 - HZK16为GB2312中文编码格式字库, 需要显示中文建议使用notepad++或其它软件(Arduino IDE 为UTF-8)通过编码GB2312打开str.h - 即可修改为个人想要输出中文字符 -*/ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Character library. 字库 +* date:2021/7/28 +******************************************************************************* + +-----PLEASE SEE THE README---- +------请在使用前看README文件----*/ #include #include "str.h" void setup() { - - M5.begin(); - - M5.Lcd.loadHzk16(); - - M5.Lcd.setTextColor(WHITE, BLACK); - + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.loadHzk16(); //Load the Chinese character library (be sure to load before using the Chinese character library). 加载汉字库(务必在使用汉字库前加载) + M5.Lcd.setTextColor(WHITE, BLACK);//Set the text color to white and the text background color to black (mandatory). 设置文字颜色为白色,文字背景颜色为黑色(必加) // Set text with red highlight color - M5.Lcd.setHighlightColor(RED); - - // Set text size to 1 - M5.Lcd.setTextSize(1); - M5.Lcd.setCursor(0,0,1); - M5.Lcd.writeHzk(AscStr); + M5.Lcd.setHighlightColor(RED);//Set the text highlight color to red. 设置文字高亮颜色为红色 + M5.Lcd.setTextSize(1);//Set text size to 1. 设置字号大小为1 Set text size to 1(必加) +} + +void loop() { + M5.Lcd.setCursor(0,0,1); //Set the cursor at (0,0) and the size to 1(mandatory). 将光标设置在(0,0)处,并设置字号为1(必加) + M5.Lcd.writeHzk(AscStr); //Display the contents of AscStr here (which can be changed in str.h). 在此处显示AscStr中的内容(可在str.h中更改) // Print some ASCII text on the screen delay(1000); // Highlight the text - M5.Lcd.highlight(true); - M5.Lcd.setCursor(0,32); - M5.Lcd.writeHzk(AscStr); - delay(1000); - // Clear screen - //M5.Lcd.clear(); - // Turn highlight off - M5.Lcd.highlight(false); - M5.Lcd.setCursor(0,70); - // print some GBK text on the screen - M5.Lcd.writeHzk(GbkStr); - + M5.Lcd.setCursor(0,40); + M5.Lcd.writeHzk(GbkStr); //Display the contents of GbkStr here (which can be changed in str.h). 在此处显示GbkStr中的内容(可在str.h中更改) delay(1000); - M5.Lcd.highlight(true); - M5.Lcd.setCursor(0,90); + //Highlight the text. 高亮显示文本 + M5.Lcd.highlight(true); //Turn on highlight. 开启高亮显示 + M5.Lcd.setCursor(0,60); M5.Lcd.writeHzk(GbkStr); - - -} - -void loop() { - // put your main code here, to run repeatedly: - + delay(1000); + M5.Lcd.fillScreen(BLACK); //Fill the screen with black color, equivalent to empty the screen. 填充屏幕颜色为黑色,等效于清空屏幕 + M5.Lcd.highlight(false); //Turn off highlight. 关闭高亮显示 + delay(500); } diff --git a/examples/Advanced/Display/HZK16/README.md b/examples/Advanced/Display/HZK16/README.md new file mode 100644 index 0000000..4788f12 --- /dev/null +++ b/examples/Advanced/Display/HZK16/README.md @@ -0,0 +1,7 @@ +HZK16 is the GB2312 Chinese encoding format font, so it is normal that GbkStr is displayed as garbled in the src.h file +To display Chinese normally, notepad++ or other software (UTF-8 for Arduino IDE) should be used to open str.h with encoding GB2312 +Modify the content in GbkStr to replace it with the character you want to display + +HZK16为GB2312中文编码格式字库, 故在 src.h 文件中 GbkStr 显示为乱码为正常现象 +若要正常显示中文应使用notepad++或其它软件(Arduino IDE 为UTF-8)使用编码GB2312打开str.h +修改GbkStr里的内容即可更换为想要显示的字符 \ No newline at end of file diff --git a/examples/Advanced/Display/HZK16/str.h b/examples/Advanced/Display/HZK16/str.h index e399ec8..5cf09a1 100644 --- a/examples/Advanced/Display/HZK16/str.h +++ b/examples/Advanced/Display/HZK16/str.h @@ -2,6 +2,6 @@ #define _STR_H_ char* AscStr="ASCII: \nABCDEFG1234567"; -char* GbkStr="ɾն"; +char* GbkStr="ɾն ˤ"; #endif diff --git a/examples/Advanced/Display/TFT_Ellipse/TFT_Ellipse.ino b/examples/Advanced/Display/TFT_Ellipse/TFT_Ellipse.ino new file mode 100644 index 0000000..f479f75 --- /dev/null +++ b/examples/Advanced/Display/TFT_Ellipse/TFT_Ellipse.ino @@ -0,0 +1,43 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Ellipse drawing. 椭圆绘制 +* date:2021/7/27 +*******************************************************************************/ + +#include + +void setup(void) { + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C +} + +void loop() { + // Draw some random ellipses. 绘制一些随机形状的椭圆 + for (int i = 0; i < 40; i++) + { + int rx = random(40); + int ry = random(40); + int x = rx + random(80 - rx - rx); + int y = ry + random(160 - ry - ry); + M5.Lcd.fillEllipse(x, y, rx, ry, random(0xFFFF)); //At (x, y), draw a filled ellipse with random width and height of rx, ry with random color random (0xFFFF). 在(x,y)处以随机颜色random(0xFFFF),绘制一个随机宽高为rx,ry的填充椭圆 + } + + delay(2000); + M5.Lcd.fillScreen(BLACK); //清空屏幕 + + for (int i = 0; i < 40; i++) + { + int rx = random(40); + int ry = random(40); + int x = rx + random(80 - rx - rx); + int y = ry + random(160 - ry - ry); + M5.Lcd.drawEllipse(x, y, rx, ry, random(0xFFFF));//At (x, y), draw an elliptical wire frame with a random width and height of rx, ry in a random color (0xFFFF). 在(x,y)处以随机颜色random(0xFFFF),绘制一个随机宽高为rx,ry的椭圆线框 + } + + delay(2000); +} \ No newline at end of file diff --git a/examples/Advanced/HallSensor/HallSensor.ino b/examples/Advanced/HallSensor/HallSensor.ino new file mode 100644 index 0000000..fd423f8 --- /dev/null +++ b/examples/Advanced/HallSensor/HallSensor.ino @@ -0,0 +1,85 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Hall sensor. 霍尔传感器 +* date:2021/7/27 +******************************************************************************* + The ESP32 MCU has build in Hall sensor. ESP32 MCU内置霍尔传感器 + It is not very suscescible but if you bring a magnet close to the + upper screen you will see the influence on the measurement. + In this example we use some low pass filters to get rid of the noise. +*/ +#include + +#define HORIZONTAL_RESOLUTION 80 //屏幕水平分辨率 +#define VERTICAL_RESOLUTION 160 //屏幕竖直分辨率 +#define POSITION_OFFSET_Y 20 + +uint16_t oldSignal[HORIZONTAL_RESOLUTION]; + +float ESP32_hallRead() //ESP32 hall value read. ESP32霍尔值读取 +{ + float value = 0; + int count = 400; + // mean value filter. 数据低通滤波器 + for (int n = 0; n < count; n++) value += hallRead(); + return value / count * 10; +} + +float HallOffset = 0; ////Store the initial value of magnetic force. 存储磁力的初值 + +void setup() +{ + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C + M5.Lcd.printf("\nESP32 Hall sensor:"); //Screen printout. 屏幕打印输出 + HallOffset = ESP32_hallRead(); // callibrate the output value to the magnetic field at start up. 在启动时将输出值校准到磁场 +} + +float LowPassFilteredValue=0; +void showSignal() +{ + int n; + + int oldx; + int oldy; + int oldSig; + int x, y; + + for (n = 0; n < HORIZONTAL_RESOLUTION; n++) //在水平分辨率内,每个像素点根据计算得到的磁力大小绘制 + { //Within the horizontal resolution, each pixel is drawn according to the calculated magnetic force + x = n; + float value = ESP32_hallRead()- HallOffset; //Reduce the influence of own magnetism. 减少自身磁力的影响 + LowPassFilteredValue+=(value-LowPassFilteredValue)*0.05; + M5.Lcd.setCursor(30, 17); + M5.Lcd.print((int)LowPassFilteredValue); + M5.Lcd.print(" "); + M5.Lcd.setCursor(0, 40); + y = map(value , -127, 127, VERTICAL_RESOLUTION, POSITION_OFFSET_Y); //将value映射至-30~30的范围内,以免绘制超出屏幕 + + if (n > 0) + { + // delete old line element. 删除上次画的线 + M5.Lcd.drawLine(oldx , oldSig, x, oldSignal[n], BLACK ); + + // draw new line element. 画新的线 + if (n < HORIZONTAL_RESOLUTION - 1) // don't draw last element because it would generate artifacts. 不能绘制最后一个像素 + { + M5.Lcd.drawLine(oldx,oldy,x,y, GREEN); + } + } + oldx = x; + oldy = y; + oldSig = oldSignal[n]; + oldSignal[n] = y; + } +} + +void loop(void) +{ + showSignal(); +} diff --git a/examples/Advanced/I2C_Tester/I2C_Tester.ino b/examples/Advanced/I2C_Tester/I2C_Tester.ino index e890394..b06994c 100644 --- a/examples/Advanced/I2C_Tester/I2C_Tester.ino +++ b/examples/Advanced/I2C_Tester/I2C_Tester.ino @@ -1,53 +1,51 @@ -/************************************************************************ - - M5StackFire I2C Scanner - - The M5StackFire has a connector for I2C devices. - This program scans the addresses 1-127 continuosly and shows - the devices found on the TFT. - - The M5Stack fire has two internal I2C devices at address 0x68 and 0x75. - - If they do not appear on the TFT it could mean you made a short cut on - the I2C bus. - - October 2018, ChrisMicro - -************************************************************************/ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:I2C Scanner. I2C探测 +* date:2021/7/27 +******************************************************************************* +*/ +/* +This program scans the addresses 1-127 continuosly and shows the devices found on the TFT. +该程序连续扫描地址 1-127 并显示在外部(内部)I2C发现的设备。 +*/ #include void setup() { - M5.Lcd.begin(); - M5.Lcd.setRotation(3); - M5.Lcd.fillScreen( BLACK ); - M5.Lcd.setCursor(0, 0); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setTextSize(1); - - M5.Lcd.fillScreen( BLACK ); - M5.Lcd.setCursor(0, 0); - M5.Lcd.println("M5StickC I2C Tester"); - - Wire.begin(); + M5.begin(); //Init M5Stick-C(Init of external I2C is also included ). 初始化M5Stick-C(初始化外部I2C也包含在内) + Wire.begin(); //Init wire and join the I2C network. 初始化wire,并且加入到I2C网络 + //Wire.begin(21, 22); //Detect internal I2C, if this sentence is not added, it will detect external I2C. 检测内部I2C,若不加此句为检测外部I2C + M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色 + M5.Lcd.getTextDatum(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.println("M5StickC I2C Tester"); //Print a string on the screen. 在屏幕上打印字符串 delay(3000); - M5.Lcd.fillScreen( BLACK ); + M5.Lcd.fillScreen( BLACK );//Make the screen full of black (equivalent to clear() to clear the screen). 使屏幕充满黑色(等效clear()清屏) } int textColor=YELLOW; -void loop() +void loop() { int address; int error; M5.Lcd.setCursor(0, 0); M5.Lcd.println("scanning Address [HEX]"); - - for(address = 1; address < 127; address++ ) + for(address = 1; address < 127; address++ ) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); + Wire.beginTransmission(address); //Data transmission to the specified device address starts. 开始向指定的设备地址进行传输数据 + error = Wire.endTransmission();/*Stop data transmission with the slave. 停止与从机的数据传输 + 0: success. 成功 + 1: The amount of data exceeds the transmission buffer capacity limit. 数据量超过传送缓存容纳限制 + return value: 2: Received NACK when sending address. 传送地址时收到 NACK + 3: Received NACK when transmitting data. 传送数据时收到 NACK + 4: Other errors. 其它错误 */ if(error==0) { M5.Lcd.print(address,HEX);M5.Lcd.print(" "); @@ -57,7 +55,8 @@ void loop() delay(10); } - if(textColor==YELLOW) textColor=GREEN; - else textColor=YELLOW; - M5.Lcd.setTextColor(textColor,BLACK); + if(textColor==YELLOW) { + textColor=CYAN; + }else textColor=YELLOW; + M5.Lcd.setTextColor(textColor,BLACK); //Set the foreground color of the text to textColor and the background color to BLACK. 设置文字的前景色为textColor背景色为BLACK } diff --git a/examples/Advanced/MultSerial/MultSerial.ino b/examples/Advanced/MultSerial/MultSerial.ino new file mode 100644 index 0000000..cde64f0 --- /dev/null +++ b/examples/Advanced/MultSerial/MultSerial.ino @@ -0,0 +1,31 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:MultSerial. 多串口 +* date:2021/8/5 +****************************************************************************** +*/ +#include + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + // Serial2.begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert) + Serial2.begin(115200, SERIAL_8N1, 16, 17); //Init serial port 2. 初始化串口2 +} + +void loop() { + if(Serial.available()) { //If the serial port reads data. 如果串口读到数据 + int ch = Serial.read(); // Copy the data read from the serial port to the CH. 把串口读取到的数据复制给ch + Serial2.write(ch); //Serial port 2 Outputs the CH content. 串口2输出ch的内容 + } + + if(Serial2.available()) { + int ch = Serial2.read(); + Serial.write(ch); + } +} diff --git a/examples/Advanced/MultiTask/MultiTask.ino b/examples/Advanced/MultiTask/MultiTask.ino new file mode 100644 index 0000000..ff710ad --- /dev/null +++ b/examples/Advanced/MultiTask/MultiTask.ino @@ -0,0 +1,74 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:MultiTask. 多线程 +* date:2021/7/27 +******************************************************************************* +*/ + +#include + +void task1(void * pvParameters) { //Define the tasks to be executed in thread 1. 定义线程1内要执行的任务 + while(1){ //Keep the thread running. 使线程一直运行 + Serial.print("task1 Uptime (ms): "); + Serial.println(millis()); //The running time of the serial port printing program. 串口打印程序运行的时间 + delay(100); //With a delay of 100ms, it can be seen in the serial monitor that every 100ms, thread 1 will be executed once. 延迟100ms,在串口监视器内可看到每隔100ms,线程1就会被执行一次 + } +} + +void task2(void * pvParameters) { + while(1){ + Serial.print("task2 Uptime (ms): "); + Serial.println(millis()); + delay(200); + } +} + +void task3(void * pvParameters) { + while(1){ + Serial.print("task3 Uptime (ms): "); + Serial.println(millis()); + delay(1000); + } +} + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + // Creat Task1. 创建线程1 + xTaskCreatePinnedToCore( + task1, //Function to implement the task. 线程对应函数名称(不能有返回值) + "task1", //线程名称 + 4096, // The size of the task stack specified as the number of * bytes.任务堆栈的大小(字节) + NULL, // Pointer that will be used as the parameter for the task * being created. 创建作为任务输入参数的指针 + 1, // Priority of the task. 任务的优先级 + NULL, // Task handler. 任务句柄 + 0); // Core where the task should run. 将任务挂载到指定内核 + + // Task 2 + xTaskCreatePinnedToCore( + task2, + "task2", + 4096, + NULL, + 2, + NULL, + 0); + + // Task 3 + xTaskCreatePinnedToCore( + task3, + "task3", + 4096, + NULL, + 3, + NULL, + 0); +} + +void loop() { +} \ No newline at end of file diff --git a/examples/Advanced/QRCode/QRCode.ino b/examples/Advanced/QRCode/QRCode.ino index ba68c1b..1f0b192 100644 --- a/examples/Advanced/QRCode/QRCode.ino +++ b/examples/Advanced/QRCode/QRCode.ino @@ -1,12 +1,23 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:QRcode. 创建二维码 +* date:2021/7/27 +******************************************************************************* +*/ + #include void setup() { - M5.begin(); - - // Display QRCode - M5.Lcd.qrcode("http://www.m5stack.com"); - // M5.Lcd.qrcode(const char *string, uint16_t x = 50, uint16_t y = 10, uint8_t width = 220, uint8_t version = 6); + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C + M5.Lcd.qrcode("http://www.m5stack.com",0,0,80); //Create a QR code with a width of 80 QR code at (0, 0). 在(0,0)处创建一个宽为80二维码 + //Please select the appropriate QR code version according to the number of characters. 请根据字符数量选择合适的二维码版本 } void loop() { -} +} \ No newline at end of file diff --git a/examples/Advanced/Storage/Counter/Counter.ino b/examples/Advanced/Storage/Counter/Counter.ino new file mode 100644 index 0000000..1f1990e --- /dev/null +++ b/examples/Advanced/Storage/Counter/Counter.ino @@ -0,0 +1,37 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:counter. 计数器 +* date:2021/8/3 +******************************************************************************* +*/ + +#include +#include + +Preferences preferences; + +void setup() { + M5.begin(); //Init M5Stick. 初始化 M5Stick + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + preferences.begin("my-app", false); //We will open storage in RW-mode (second parameter has to be false). 在perferences中创建叫my-app的空间,并以rw模式打开存储(第二个参数必须为false) + + //preferences.clear(); // Remove all preferences under the opened namespace.清除preferences中所有的空间 + + //preferences.remove("counter"); // Or remove the counter key only. 只清除counter中的值 + + unsigned int counter = preferences.getUInt("counter", 0); //Get the counter value in current sapce, if the key does not exist, return a default value of 0. 在当前空间中读取counter的值(若不存在为0),并赋值给counter + counter++; //Increase counter by 1. 使计数器的值加一 + M5.Lcd.printf("Current counter value: %u\n", counter); // Print the counter to Serial Monitor. 串口输出计数器的值 + preferences.putUInt("counter", counter); // Store the counter to the Preferences. 存储计数器的值 + preferences.end(); // Close the Preferences. 关闭Preferences + M5.Lcd.println("Restarting in 10 seconds.."); + delay(10000); //delay 10. 延迟10s + ESP.restart(); // Restart. 重启 +} +void loop() {} diff --git a/examples/Advanced/Storage/EEPROM/EEPROM.ino b/examples/Advanced/Storage/EEPROM/EEPROM.ino new file mode 100644 index 0000000..568a594 --- /dev/null +++ b/examples/Advanced/Storage/EEPROM/EEPROM.ino @@ -0,0 +1,59 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:EEPROM Read & Write. +* date:2021/7/30 +******************************************************************************* + The values stored in the EEPROM will remain in the EEPROM even after the M5Stick is disconnected. + When a new program is uploaded to the M5Stick, the values stored in the EEPROM can still be called or modified by the new program. + 储存于EEPROM的数值即使在断开 M5Stick电源后仍会保存在EEPROM中 + 当新程序上传到 M5Stick后,储存于EEPROM中的数值仍然可以被新的程序调用或者修改 +*/ + +#include +#include + +int addr = 0; //EEPROM Start number of an ADDRESS. EEPROM地址起始编号 +#define SIZE 32 //define the size of EEPROM(Byte). 定义EEPROM的大小(字节) + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.println("\n\nPlease open serial"); + if (!EEPROM.begin(SIZE)){ //Request storage of SIZE size(success return 1). 申请SIZE大小的存储(成功返回1) + Serial.println("\nFailed to initialise EEPROM!"); //串口输出格式化字符串. Serial output format string + delay(1000000); + } + Serial.println("\nRead data from EEPROM. Values are:"); + for (int i = 0; i < SIZE; i++){ + Serial.printf("%d ",EEPROM.read(i)); //Reads data from 0 to SIZE in EEPROM. 读取EEPROM中从0到SIZE中的数据 + } + Serial.println("\n\nPress BtnA to Write EEPROM"); +} + +void loop() { + M5.update(); //Check button down state. 检测按键按下状态 + if(M5.BtnA.isPressed()){ //if the button is Pressed. 如果按键按下 + Serial.printf("\n%d Bytes datas written on EEPROM.\nValues are:\n",SIZE); + for(int i=0;i +#include +String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称 +bool SPIFFS_FORMAT = false; //Whether to initialize the SPIFFS. 是否初始化SPIFFS +//You don't need to format the flash file system every time you use it. +//无需每次使用闪存都进行格式化 + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + if(SPIFFS_FORMAT){ + M5.Lcd.println("SPIFFS format start..."); //output format String in LCD. 在屏幕上输出格式化字符串 + SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS + M5.Lcd.println("SPIFFS format finish"); + } + + if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1 + M5.Lcd.println("SPIFFS Begin."); + //Write operation + File dataFile = SPIFFS.open(file_name, "w"); // Create a File object dafa File to write information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name写入信息 + dataFile.println("Hello IOT World."); // Writes string information and newlines to the dataFile. 向dataFile写入字符串信息并换行 + dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件 + M5.Lcd.println("Finished Writing data to SPIFFS"); + } else { + M5.Lcd.println("SPIFFS Failed to Begin.\nYou need to Run SPIFFS_Add.ino first"); + } +} + +void loop() { + M5.update(); //Check whether the key is pressed. 检测按键是否按下 + if(M5.BtnA.isPressed()){ //If the button is pressed. 如果按键按下 + if (SPIFFS.exists(file_name)){ //Check whether the file_name file exists in the flash memory. 确认闪存中是否有file_name文件 + M5.Lcd.println("FOUND."); + M5.Lcd.println(file_name); + } else { + M5.Lcd.println("NOT FOUND."); + M5.Lcd.println(file_name); + } + File dataFile = SPIFFS.open(file_name, "r"); // Create aFile object dafaFile to read information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name读取信息 + for(int i=0; i +#include + +String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称 +bool SPIFFS_FORMAT = true; //Whether to initialize the SPIFFS. 是否初始化SPIFFS +//You don't need to format the flash file system every time you use it. +//无需每次使用闪存都进行格式化 + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + if(SPIFFS_FORMAT){ + M5.Lcd.println("\nSPIFFS format start..."); //Screen prints format String. 屏幕打印格式化字符串 + SPIFFS.format(); // Formatting SPIFFS. 格式化SPIFFS + M5.Lcd.println("SPIFFS format finish"); + } + if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1 + M5.Lcd.println("\nSPIFFS Started."); + } else { + M5.Lcd.println("SPIFFS Failed to Start."); + } + + if (SPIFFS.exists(file_name)){ //Check whether the file_name file exists in the flash memory. 确认闪存中是否有file_name文件 + M5.Lcd.println("FOUND."); + M5.Lcd.println(file_name); + + File dataFile = SPIFFS.open(file_name, "a"); // Create a File object dafaFile to add information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name添加信息 + dataFile.println("This is Appended Info."); // Adds string information to dataFile. 向dataFile添加字符串信息 + dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件 + M5.Lcd.println("Finished Appending data to SPIFFS"); + }else { + M5.Lcd.println("NOT FOUND."); + M5.Lcd.print(file_name); + M5.Lcd.println("is creating."); + File dataFile = SPIFFS.open(file_name, "w"); // Create aFile object dafaFile to write information to file_name in the SPIFFS. 建立File对象dafaFile用于向SPIFFS中的file_name写入信息 + dataFile.close(); // Close the file when writing is complete. 完成写入后关闭文件 + M5.Lcd.println("Please disable format and Reupload"); + } +} + +void loop() { +} \ No newline at end of file diff --git a/examples/Advanced/Storage/SPIFFS/SPIFFS_Delete/SPIFFS_Delete.ino b/examples/Advanced/Storage/SPIFFS/SPIFFS_Delete/SPIFFS_Delete.ino new file mode 100644 index 0000000..985cd34 --- /dev/null +++ b/examples/Advanced/Storage/SPIFFS/SPIFFS_Delete/SPIFFS_Delete.ino @@ -0,0 +1,37 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:SPIFFS Delete +* date:2021/8/4 +****************************************************************************** +*/ + +#include +#include + +String file_name = "/M5Stack/notes.txt"; //Sets the location and name of the file to be operated on. 设置被操作的文件位置和名称 +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + if(SPIFFS.begin()){ // Start SPIFFS, return 1 on success. 启动闪存文件系统,若成功返回1 + M5.Lcd.println("\nSPIFFS Started."); //Screen prints format String. 屏幕打印格式化字符串 + }else{ + M5.Lcd.println("SPIFFS Failed to Start."); + } + + if(SPIFFS.remove(file_name)){ //Delete file_name file from flash, return 1 on success. 从闪存中删除file_name文件,如果成功返回1 + M5.Lcd.print(file_name); + M5.Lcd.println(" Remove sucess"); + }else{ + M5.Lcd.print(file_name); + M5.Lcd.println(" Remove fail"); + } +} + +void loop() { +} \ No newline at end of file diff --git a/examples/Advanced/Time/Time.ino b/examples/Advanced/Time/Time.ino new file mode 100644 index 0000000..2fccb62 --- /dev/null +++ b/examples/Advanced/Time/Time.ino @@ -0,0 +1,55 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:NTP TIME. +* date:2021/8/3 +*******************************************************************************/ + +#include +#include +#include "time.h" + +// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码 +const char* ssid = "M5"; +const char* password = "123456"; + +const char* ntpServer = "time1.aliyun.com"; //Set the connect NTP server. 设置连接的NTP服务器 +const long gmtOffset_sec = 0; +const int daylightOffset_sec = 3600; + +void printLocalTime(){ //Output current time. 输出当前时间 + struct tm timeinfo; + if(!getLocalTime(&timeinfo)){ //Return 1 when the time is successfully obtained. 成功获取到时间返回1 + M5.Lcd.println("Failed to obtain time"); + return; + } + M5.Lcd.println(&timeinfo, "%A, %B %d \n%Y %H:%M:%S"); //Screen prints date and time. 屏幕打印日期和时间 +} + +void setup(){ + M5.begin(); //Init M5Stick. 初始化M5Stick + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.printf("\nConnecting to %s", ssid); + WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态 + while(WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功 + delay(500); //delay 0.5s. 延迟0.5s + M5.Lcd.print("."); + } + M5.Lcd.println("\nCONNECTED!"); + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); //init and get the time. 初始化并设置NTP + printLocalTime(); + WiFi.disconnect(true); //Disconnect wifi. 断开wifi连接 + WiFi.mode(WIFI_OFF); //Set the wifi mode to off. 设置wifi模式为关闭 + delay(20); +} + +void loop(){ + delay(1000); + M5.Lcd.setCursor(0,25); //Set cursor to (0,25). 设置光标处于(0,25) + printLocalTime(); +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/BasicHttpClient/BasicHttpClient.ino b/examples/Advanced/WIFI/BasicHttpClient/BasicHttpClient.ino new file mode 100644 index 0000000..ca553a6 --- /dev/null +++ b/examples/Advanced/WIFI/BasicHttpClient/BasicHttpClient.ino @@ -0,0 +1,52 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:BasicHTTPClient. +* date:2021/8/4 +****************************************************************************** +*/ +#include +#include +#include +#include +#include + +WiFiMulti wifiMulti; +HTTPClient http; + +void setup() { + M5.begin(); //Init M5Stick. 初始化 M5Stick + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + wifiMulti.addAP("M5", "41231243"); //Storage wifi configuration information. 存储wifi配置信息 + M5.Lcd.print("\nConnecting Wifi...\n"); //print format output string on lcd. 串口格式化输出字符串 +} + +void loop() { + M5.Lcd.setCursor(0,0); //Set the cursor at (0,0). 设置光标位于(0,0)处 + if((wifiMulti.run() == WL_CONNECTED)) { // wait for WiFi connection. 等待连接至wifi + M5.Lcd.print("[HTTP] begin...\n"); + http.begin("http://example.com/index.html"); // configure traged server and url. 配置被跟踪的服务器和URL + M5.Lcd.print("[HTTP] GET...\n"); + int httpCode = http.GET(); // start connection and send HTTP header. 开始连接服务器并发送HTTP的标头 + if(httpCode > 0) { // httpCode will be negative on error. 出错时httpCode将为负值 + Serial.printf("[HTTP] GET... code: %d\n", httpCode); + M5.Lcd.print("Please see Serial."); + if(httpCode == HTTP_CODE_OK) { // file found at server. 在服务器上找到文件 + String payload = http.getString(); + Serial.println(payload); //打印在服务器上读取的文件. Print files read on the server + } + }else { + M5.Lcd.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + http.end(); + }else{ + M5.Lcd.print("connect failed"); + } + delay(5000); + M5.Lcd.fillRect(0,0,160,80,BLACK); //clear the screen. 清除屏幕 +} diff --git a/examples/Advanced/WIFI/OTAUpload/OTAUpload.ino b/examples/Advanced/WIFI/OTAUpload/OTAUpload.ino new file mode 100644 index 0000000..68fa078 --- /dev/null +++ b/examples/Advanced/WIFI/OTAUpload/OTAUpload.ino @@ -0,0 +1,53 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:OTA Upload. 隔空传输程序 +* date:2021/7/30 +******************************************************************************* + PC and M5Stick-C can only be used on the same wifi. 电脑和M5Stick-C需在同一wifi下才可使用 + When the OTA is ready, restart the Arduino client from Tools > Ports > Network ports to instantly transmit the program wirelessly. + OTA 准备好后重启Arduino客户端在工具->端口->网络端口,即刻无线传输程序 +*/ +#include +#include +#include + +// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码 +const char* ssid = "wifi"; +const char* password = "123456123456"; + +void setup() { + M5.begin(); //Init M5Stick-C. 初始化 M5Stick-C + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态 + M5.lcd.print("Waiting Wifi Connect"); + while (WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功 + delay(1000); + M5.lcd.print("."); + } + M5.lcd.println("\nWiFi Connected!"); + M5.lcd.print("WiFi Connect To: "); + M5.lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称 + M5.lcd.print("IP address: "); + M5.lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址 + + ArduinoOTA.setHostname("M5StickC"); //Set the network port name. 设置网络端口名称 + ArduinoOTA.setPassword("666666"); //Set the network port connection password. 设置网络端口连接的密码 + ArduinoOTA.begin(); //Initialize the OTA. 初始化OTA + M5.lcd.println("OTA ready!"); //M5.lcd port output format string. 串口输出格式化字符串 +} + +void loop() { + ArduinoOTA.handle(); //Continuously check for update requests. 持续检测是否有更新请求 + M5.update(); + if(M5.BtnA.isPressed()){ //if BtnA is Pressed. 如果按键A按下 + ArduinoOTA.end(); //Ends the ArduinoOTA service. 结束OTA服务 + M5.lcd.println("OTA End!"); + delay(200); + } +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/WiFiAccessPoint/WiFiAccessPoint.ino b/examples/Advanced/WIFI/WiFiAccessPoint/WiFiAccessPoint.ino new file mode 100644 index 0000000..e50a26c --- /dev/null +++ b/examples/Advanced/WIFI/WiFiAccessPoint/WiFiAccessPoint.ino @@ -0,0 +1,94 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:WIFI AP. wifi热点 +* date:2021/7/29 +******************************************************************************* + WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it. + 创建一个WiFi接入点,并在其上提供一个网络服务器 + And can send requests to M5Stick-C through the web page + 并可通过网页向M5Stick-C发送请求 +*/ + +#include +#include +#include +#include + +// Set these to your desired credentials. 设置你的热点名称和密码 +const char *ssid = "M5Stack_Ap"; +const char *password = "66666666"; + +WiFiServer server(80); + +void setup() { + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C + M5.lcd.rotation(3); + M5.lcd.println("WIFI ACCESS POINT"); //Screen print string. 屏幕打印字符串 + M5.lcd.printf("Please connect:%s \nThen access to:",ssid); + WiFi.softAP(ssid, password); // You can remove the password parameter if you want the AP to be open. 如果你想建立开放式热点,可以删除密码 + IPAddress myIP = WiFi.softAPIP(); //Get the softAP interface IP address. 获取AP接口IP地址 + M5.lcd.println(myIP); + server.begin(); //Start the established Internet of Things network server. 启动建立的物联网网络服务器 +} + +void loop() { + WiFiClient client = server.available(); // listen for incoming clients. 检查有没有设备通过网络向M5Stack网络服务器发送请求 + + if (client) { // if you get a client. 如果收到请求 + M5.lcd.print("New Client:"); + String currentLine = ""; // make a String to hold incoming data from the client. 创建一个String来保存来自客户端的传入数据 + while (client.connected()) { // loop while the client's connected,continuously receiving data. 在客户端连接时进行循环,不断接收数据 + if (client.available()) { // if there's bytes to read from the client. 如果有数据可读取 + char c = client.read(); // store the read a byte. 存储读取到的数据 + Serial.write(c); + if (c == '\n') { // if the byte is a newline character. 如果读取到的字节为换行符 + // \n is the end of the client'S HTTP request, indicating that the client has sent a new request + // \n 是客户端HTTP请求的结尾,说明客户端发来新请求: + if (currentLine.length() == 0) { + //Here are the instructions to create a page. 下面是创建一个页面的指令 + + //HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) + //HTTP的开头总是以响应代码开始(例如 HTTP/1.1 200 OK) + //and a content-type so the client knows what's coming, then a blank line: + //然后是content-type,这样客户端就知道接下来会发生什么,然后是空行: + client.println("HTTP/1.1 200 OK"); + client.println("Content-type:text/html"); + client.println(); + + // the content of the HTTP response follows the header: + // HTTP页面显示的内容跟在开头后面: + // /High and /Low are the data received when clicking the corresponding connection, which can be replaced. + // /High和/Low 为点击对应连接时接收到的数据,可更换 + client.print("Click here to turn ON the LED.
"); + client.print("Click here to turn OFF the LED.
"); + + // The HTTP response ends with another blank line: + // HTTP响应以空行结束: + client.println(); + // break out of the while loop:退出循环 + break; + } else { // if you got a newline, then clear currentLine:如果得到新的一行,那么清除当前行 + currentLine = ""; + } + } else if (c != '\r') { // if you got anything else but a carriage return character. 如果你得到了除了回车符以外的其他字符, + currentLine += c; // add it to the end of the currentLine. 将它添加到currentLine的末尾 + } + + // Check to see if the client request was "GET /H" or "GET /L": + // 检查客户端请求是“GET /High”还是“GET /Low”: + if (currentLine.endsWith("GET /High")) { + M5.Lcd.print("ON\n"); + }else if (currentLine.endsWith("GET /Low")) { + M5.Lcd.print("OFF\n"); + } + } + } + client.stop(); // close the connection. 关闭连接 + } +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/WiFiMulti/WiFiMulti.ino b/examples/Advanced/WIFI/WiFiMulti/WiFiMulti.ino new file mode 100644 index 0000000..85885af --- /dev/null +++ b/examples/Advanced/WIFI/WiFiMulti/WiFiMulti.ino @@ -0,0 +1,49 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:WIFI Multi. 多wifi择优 +* date:2021/7/29 +******************************************************************************* + * Connect to the best AP based on a given wifi list + * 根据给定wifi的列表连接到最好的AP + */ + +#include +#include +#include + +WiFiMulti wifiMulti; + +void setup() +{ + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + wifiMulti.addAP("wifi1", "213123"); //Storage wifi configuration information 1. 存储wifi配置信息1 + wifiMulti.addAP("M5wifi", "213431241234"); + wifiMulti.addAP("aaa", "sadf"); + M5.lcd.print("Connecting Wifi..."); //Serial port format output string. 串口格式化输出字符串 +} + +void loop() +{ + if(wifiMulti.run() == WL_CONNECTED) { //If the connection to wifi is established successfully. 如果与wifi成功建立连接 + M5.lcd.setCursor(0,20); + M5.lcd.print("WiFi connected\n\nSSID:"); + M5.lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称 + M5.lcd.print("RSSI: "); + M5.lcd.println(WiFi.RSSI()); //Output signal strength. 输出信号强度 + M5.lcd.print("IP address: "); + M5.lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址 + delay(1000); + M5.lcd.fillRect(0,20,180,300,BLACK); //It's equivalent to partial screen clearance. 相当于部分清屏 + }else{ + //If the connection to wifi is not established successfully. 如果没有与wifi成功建立连接 + M5.lcd.print("."); + delay(1000); + } +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/WiFiScan/WiFiScan.ino b/examples/Advanced/WIFI/WiFiScan/WiFiScan.ino new file mode 100644 index 0000000..fccda15 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiScan/WiFiScan.ino @@ -0,0 +1,56 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Wifi scan. wifi扫描 +* date:2021/7/28 +******************************************************************************* +*/ +#include +#include "WiFi.h" + +void setup() +{ + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + WiFi.mode(WIFI_STA);// Set WiFi to station mode and disconnect from an AP if it was previously connected. 将WiFi设置为站模式,如果之前连接过AP,则断开连接 + WiFi.disconnect(); //Turn off all wifi connections. 关闭所有wifi连接 + delay(100); //100 ms delay. 延迟100ms + M5.Lcd.print("WIFI SCAN"); //Screen print string. 屏幕打印字符串 +} + +void loop() +{ + M5.Lcd.setCursor(0,0);//Set the cursor at (0,0). 将光标设置在(0,0)处 + M5.Lcd.println("Please press Btn.A to (re)scan"); + M5.update(); //Check the status of the key. 检测按键的状态 + if(M5.BtnA.isPressed()){ //If button A is pressed. 如果按键A按下 + M5.Lcd.fillScreen(BLACK); //Clear the screen. 清空屏幕 + M5.Lcd.setCursor(0,0); + M5.Lcd.println("scan start\n"); + M5.Lcd.setCursor(0,0); + int n = WiFi.scanNetworks(); //return the number of networks found. 返回发现的网络数 + if (n == 0){ //If no network is found. 如果没有找到网络 + M5.Lcd.println("no networks found"); + }else{ //If have network is found. 找到网络 + for(int j=0;j<(n/7)+1;j++){ + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setCursor(0,0); + M5.Lcd.printf("networks list %d:,found:%d\n\n",j+1,n); + for (int i = 0; i < 7&&(i + 1+(j*7)<=n); ++i){ + // Print SSID and RSSI for each network found. 打印每个找到的网络的SSID和信号强度 + M5.Lcd.printf("%d:",i + 1+(j*7)); + M5.Lcd.print(WiFi.SSID(i)); + M5.Lcd.printf("(%d)",WiFi.RSSI(i)); + M5.Lcd.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); + } + delay(6000); + } + } + delay(1000); + } +} diff --git a/examples/Advanced/WIFI/WiFiSetting/Parsing.cpp b/examples/Advanced/WIFI/WiFiSetting/Parsing.cpp new file mode 100644 index 0000000..a14e6ab --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/Parsing.cpp @@ -0,0 +1,607 @@ +/* + Parsing.cpp - HTTP request parsing. + + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "WebServer.h" + +//#define DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif + +static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +{ + char *buf = nullptr; + dataLength = 0; + while (dataLength < maxLength) { + int tries = timeout_ms; + size_t newLength; + while (!(newLength = client.available()) && tries--) delay(1); + if (!newLength) { + break; + } + if (!buf) { + buf = (char *) malloc(newLength + 1); + if (!buf) { + return nullptr; + } + } + else { + char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); + if (!newBuf) { + free(buf); + return nullptr; + } + buf = newBuf; + } + client.readBytes(buf + dataLength, newLength); + dataLength += newLength; + buf[dataLength] = '\0'; + } + return buf; +} + +bool WebServer::_parseRequest(WiFiClient& client) { + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + //reset header value + for (int i = 0; i < _headerKeysCount; ++i) { + _currentHeaders[i].value =String(); + } + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Invalid request: "); + DEBUG_OUTPUT.println(req); +#endif + return false; + } + + String methodStr = req.substring(0, addr_start); + String url = req.substring(addr_start + 1, addr_end); + String versionEnd = req.substring(addr_end + 8); + _currentVersion = atoi(versionEnd.c_str()); + String searchStr = ""; + int hasSearch = url.indexOf('?'); + if (hasSearch != -1){ + searchStr = urlDecode(url.substring(hasSearch + 1)); + url = url.substring(0, hasSearch); + } + _currentUri = url; + _chunked = false; + + HTTPMethod method = HTTP_GET; + if (methodStr == "POST") { + method = HTTP_POST; + } else if (methodStr == "DELETE") { + method = HTTP_DELETE; + } else if (methodStr == "OPTIONS") { + method = HTTP_OPTIONS; + } else if (methodStr == "PUT") { + method = HTTP_PUT; + } else if (methodStr == "PATCH") { + method = HTTP_PATCH; + } + _currentMethod = method; + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("method: "); + DEBUG_OUTPUT.print(methodStr); + DEBUG_OUTPUT.print(" url: "); + DEBUG_OUTPUT.print(url); + DEBUG_OUTPUT.print(" search: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + //attach handler + RequestHandler* handler; + for (handler = _firstHandler; handler; handler = handler->next()) { + if (handler->canHandle(_currentMethod, _currentUri)) + break; + } + _currentHandler = handler; + + String formData; + // below is needed only when POST type request + if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ + String boundaryStr; + String headerName; + String headerValue; + bool isForm = false; + bool isEncoded = false; + uint32_t contentLength = 0; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 1); + headerValue.trim(); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase("Content-Type")){ + if (headerValue.startsWith("text/plain")){ + isForm = false; + } else if (headerValue.startsWith("application/x-www-form-urlencoded")){ + isForm = false; + isEncoded = true; + } else if (headerValue.startsWith("multipart/")){ + boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); + isForm = true; + } + } else if (headerName.equalsIgnoreCase("Content-Length")){ + contentLength = headerValue.toInt(); + } else if (headerName.equalsIgnoreCase("Host")){ + _hostHeader = headerValue; + } + } + + if (!isForm){ + size_t plainLength; + char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); + if (plainLength < contentLength) { + free(plainBuf); + return false; + } + if (contentLength > 0) { + if (searchStr != "") searchStr += '&'; + if(isEncoded){ + //url encoded form + String decoded = urlDecode(plainBuf); + size_t decodedLen = decoded.length(); + memcpy(plainBuf, decoded.c_str(), decodedLen); + plainBuf[decodedLen] = 0; + searchStr += plainBuf; + } + _parseArguments(searchStr); + if(!isEncoded){ + //plain post json or other data + RequestArgument& arg = _currentArgs[_currentArgCount++]; + arg.key = "plain"; + arg.value = String(plainBuf); + } + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Plain: "); + DEBUG_OUTPUT.println(plainBuf); + #endif + free(plainBuf); + } + } + + if (isForm){ + _parseArguments(searchStr); + if (!_parseForm(client, boundaryStr, contentLength)) { + return false; + } + } + } else { + String headerName; + String headerValue; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 2); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase("Host")){ + _hostHeader = headerValue; + } + } + _parseArguments(searchStr); + } + client.flush(); + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Request: "); + DEBUG_OUTPUT.println(url); + DEBUG_OUTPUT.print(" Arguments: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + return true; +} + +bool WebServer::_collectHeader(const char* headerName, const char* headerValue) { + for (int i = 0; i < _headerKeysCount; i++) { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { + _currentHeaders[i].value=headerValue; + return true; + } + } + return false; +} + +void WebServer::_parseArguments(String data) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args: "); + DEBUG_OUTPUT.println(data); +#endif + if (_currentArgs) + delete[] _currentArgs; + _currentArgs = 0; + if (data.length() == 0) { + _currentArgCount = 0; + _currentArgs = new RequestArgument[1]; + return; + } + _currentArgCount = 1; + + for (int i = 0; i < (int)data.length(); ) { + i = data.indexOf('&', i); + if (i == -1) + break; + ++i; + ++_currentArgCount; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + + _currentArgs = new RequestArgument[_currentArgCount+1]; + int pos = 0; + int iarg; + for (iarg = 0; iarg < _currentArgCount;) { + int equal_sign_index = data.indexOf('=', pos); + int next_arg_index = data.indexOf('&', pos); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("pos "); + DEBUG_OUTPUT.print(pos); + DEBUG_OUTPUT.print("=@ "); + DEBUG_OUTPUT.print(equal_sign_index); + DEBUG_OUTPUT.print(" &@ "); + DEBUG_OUTPUT.println(next_arg_index); +#endif + if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg missing value: "); + DEBUG_OUTPUT.println(iarg); +#endif + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + continue; + } + RequestArgument& arg = _currentArgs[iarg]; + arg.key = data.substring(pos, equal_sign_index); + arg.value = data.substring(equal_sign_index + 1, next_arg_index); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg "); + DEBUG_OUTPUT.print(iarg); + DEBUG_OUTPUT.print(" key: "); + DEBUG_OUTPUT.print(arg.key); + DEBUG_OUTPUT.print(" value: "); + DEBUG_OUTPUT.println(arg.value); +#endif + ++iarg; + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + } + _currentArgCount = iarg; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + +} + +void WebServer::_uploadWriteByte(uint8_t b){ + if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.totalSize += _currentUpload.currentSize; + _currentUpload.currentSize = 0; + } + _currentUpload.buf[_currentUpload.currentSize++] = b; +} + +uint8_t WebServer::_uploadReadByte(WiFiClient& client){ + int res = client.read(); + if(res == -1){ + while(!client.available() && client.connected()) + yield(); + res = client.read(); + } + return (uint8_t)res; +} + +bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ + (void) len; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Parse Form: Boundary: "); + DEBUG_OUTPUT.print(boundary); + DEBUG_OUTPUT.print(" Length: "); + DEBUG_OUTPUT.println(len); +#endif + String line; + int retry = 0; + do { + line = client.readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client.readStringUntil('\n'); + //start reading the form + if (line == ("--"+boundary)){ + RequestArgument* postArgs = new RequestArgument[32]; + int postArgsLen = 0; + while(1){ + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ + int nameStart = line.indexOf('='); + if (nameStart != -1){ + argName = line.substring(nameStart+2); + nameStart = argName.indexOf('='); + if (nameStart == -1){ + argName = argName.substring(0, argName.length() - 1); + } else { + argFilename = argName.substring(nameStart+2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg FileName: "); + DEBUG_OUTPUT.println(argFilename); +#endif + //use GET to set the filename if uploading using blob + if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Name: "); + DEBUG_OUTPUT.println(argName); +#endif + argType = "text/plain"; + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){ + argType = line.substring(line.indexOf(':')+2); + //skip next line + client.readStringUntil('\r'); + client.readStringUntil('\n'); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Type: "); + DEBUG_OUTPUT.println(argType); +#endif + if (!argIsFile){ + while(1){ + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("--"+boundary)) break; + if (argValue.length() > 0) argValue += "\n"; + argValue += line; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Value: "); + DEBUG_OUTPUT.println(argValue); + DEBUG_OUTPUT.println(); +#endif + + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = argName; + arg.value = argValue; + + if (line == ("--"+boundary+"--")){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + } else { + _currentUpload.status = UPLOAD_FILE_START; + _currentUpload.name = argName; + _currentUpload.filename = argFilename; + _currentUpload.type = argType; + _currentUpload.totalSize = 0; + _currentUpload.currentSize = 0; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Start File: "); + DEBUG_OUTPUT.print(_currentUpload.filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.println(_currentUpload.type); +#endif + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.status = UPLOAD_FILE_WRITE; + uint8_t argByte = _uploadReadByte(client); +readfile: + while(argByte != 0x0D){ + if (!client.connected()) return _parseFormUploadAborted(); + _uploadWriteByte(argByte); + argByte = _uploadReadByte(client); + } + + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if (argByte == 0x0A){ + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + goto readfile; + } else { + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + goto readfile; + } + } + + uint8_t endBuf[boundary.length()]; + client.readBytes(endBuf, boundary.length()); + + if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.totalSize += _currentUpload.currentSize; + _currentUpload.status = UPLOAD_FILE_END; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("End File: "); + DEBUG_OUTPUT.print(_currentUpload.filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.print(_currentUpload.type); + DEBUG_OUTPUT.print(" Size: "); + DEBUG_OUTPUT.println(_currentUpload.totalSize); +#endif + line = client.readStringUntil(0x0D); + client.readStringUntil(0x0A); + if (line == "--"){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + continue; + } else { + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + _uploadWriteByte((uint8_t)('-')); + uint32_t i = 0; + while(i < boundary.length()){ + _uploadWriteByte(endBuf[i++]); + } + argByte = _uploadReadByte(client); + goto readfile; + } + } else { + _uploadWriteByte(0x0D); + goto readfile; + } + break; + } + } + } + } + + int iarg; + int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++){ + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; + } + if (_currentArgs) delete[] _currentArgs; + _currentArgs = new RequestArgument[postArgsLen]; + for (iarg = 0; iarg < postArgsLen; iarg++){ + RequestArgument& arg = _currentArgs[iarg]; + arg.key = postArgs[iarg].key; + arg.value = postArgs[iarg].value; + } + _currentArgCount = iarg; + if (postArgs) delete[] postArgs; + return true; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Error: line: "); + DEBUG_OUTPUT.println(line); +#endif + return false; +} + +String WebServer::urlDecode(const String& text) +{ + String decoded = ""; + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + while (i < len) + { + char decodedChar; + char encodedChar = text.charAt(i++); + if ((encodedChar == '%') && (i + 1 < len)) + { + temp[2] = text.charAt(i++); + temp[3] = text.charAt(i++); + + decodedChar = strtol(temp, NULL, 16); + } + else { + if (encodedChar == '+') + { + decodedChar = ' '; + } + else { + decodedChar = encodedChar; // normal ascii char + } + } + decoded += decodedChar; + } + return decoded; +} + +bool WebServer::_parseFormUploadAborted(){ + _currentUpload.status = UPLOAD_FILE_ABORTED; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + return false; +} diff --git a/examples/Advanced/WIFI/WiFiSetting/WebServer.cpp b/examples/Advanced/WIFI/WiFiSetting/WebServer.cpp new file mode 100644 index 0000000..0403ed4 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/WebServer.cpp @@ -0,0 +1,525 @@ +/* + WebServer.cpp - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + + +#include +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "WebServer.h" +#include "FS.h" +#include "detail/RequestHandlersImpl.h" + +//#define DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif + +const char * AUTHORIZATION_HEADER = "Authorization"; + +WebServer::WebServer(IPAddress addr, int port) +: _server(addr, port) +, _currentMethod(HTTP_ANY) +, _currentVersion(0) +, _currentStatus(HC_NONE) +, _statusChange(0) +, _currentHandler(0) +, _firstHandler(0) +, _lastHandler(0) +, _currentArgCount(0) +, _currentArgs(0) +, _headerKeysCount(0) +, _currentHeaders(0) +, _contentLength(0) +, _chunked(false) +{ +} + +WebServer::WebServer(int port) +: _server(port) +, _currentMethod(HTTP_ANY) +, _currentVersion(0) +, _currentStatus(HC_NONE) +, _statusChange(0) +, _currentHandler(0) +, _firstHandler(0) +, _lastHandler(0) +, _currentArgCount(0) +, _currentArgs(0) +, _headerKeysCount(0) +, _currentHeaders(0) +, _contentLength(0) +, _chunked(false) +{ +} + +WebServer::~WebServer() { + if (_currentHeaders) + delete[]_currentHeaders; + _headerKeysCount = 0; + RequestHandler* handler = _firstHandler; + while (handler) { + RequestHandler* next = handler->next(); + delete handler; + handler = next; + } + close(); +} + +void WebServer::begin() { + _currentStatus = HC_NONE; + _server.begin(); + if(!_headerKeysCount) + collectHeaders(0, 0); +} + +bool WebServer::authenticate(const char * username, const char * password){ + if(hasHeader(AUTHORIZATION_HEADER)){ + String authReq = header(AUTHORIZATION_HEADER); + if(authReq.startsWith("Basic")){ + authReq = authReq.substring(6); + authReq.trim(); + char toencodeLen = strlen(username)+strlen(password)+1; + char *toencode = new char[toencodeLen + 1]; + if(toencode == NULL){ + authReq = String(); + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + if(encoded == NULL){ + authReq = String(); + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){ + authReq = String(); + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + } + authReq = String(); + } + return false; +} + +void WebServer::requestAuthentication(){ + sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); + send(401); +} + +void WebServer::on(const String &uri, WebServer::THandlerFunction handler) { + on(uri, HTTP_ANY, handler); +} + +void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) { + on(uri, method, fn, _fileUploadHandler); +} + +void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { + _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +} + +void WebServer::addHandler(RequestHandler* handler) { + _addRequestHandler(handler); +} + +void WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next(handler); + _lastHandler = handler; + } +} + +void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { + _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); +} + +void WebServer::handleClient() { + if (_currentStatus == HC_NONE) { + WiFiClient client = _server.available(); + if (!client) { + return; + } + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("New client"); +#endif + + _currentClient = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + if (!_currentClient.connected()) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + return; + } + + // Wait for data from client to become available + if (_currentStatus == HC_WAIT_READ) { + if (!_currentClient.available()) { + if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + } + yield(); + return; + } + + if (!_parseRequest(_currentClient)) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + return; + } + _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + + if (!_currentClient.connected()) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + return; + } else { + _currentStatus = HC_WAIT_CLOSE; + _statusChange = millis(); + return; + } + } + + if (_currentStatus == HC_WAIT_CLOSE) { + if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) { + _currentClient = WiFiClient(); + _currentStatus = HC_NONE; + } else { + yield(); + return; + } + } +} + +void WebServer::close() { + _server.end(); +} + +void WebServer::stop() { + close(); +} + +void WebServer::sendHeader(const String& name, const String& value, bool first) { + String headerLine = name; + headerLine += ": "; + headerLine += value; + headerLine += "\r\n"; + + if (first) { + _responseHeaders = headerLine + _responseHeaders; + } + else { + _responseHeaders += headerLine; + } +} + +void WebServer::setContentLength(size_t contentLength) { + _contentLength = contentLength; +} + +void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { + response = "HTTP/1."+String(_currentVersion)+" "; + response += String(code); + response += " "; + response += _responseCodeToString(code); + response += "\r\n"; + + if (!content_type) + content_type = "text/html"; + + sendHeader("Content-Type", content_type, true); + if (_contentLength == CONTENT_LENGTH_NOT_SET) { + sendHeader("Content-Length", String(contentLength)); + } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { + sendHeader("Content-Length", String(_contentLength)); + } else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client + //let's do chunked + _chunked = true; + sendHeader("Accept-Ranges","none"); + sendHeader("Transfer-Encoding","chunked"); + } + sendHeader("Connection", "close"); + + response += _responseHeaders; + response += "\r\n"; + _responseHeaders = String(); +} + +void WebServer::send(int code, const char* content_type, const String& content) { + String header; + // Can we asume the following? + //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) + // _contentLength = CONTENT_LENGTH_UNKNOWN; + _prepareHeader(header, code, content_type, content.length()); + _currentClient.write(header.c_str(), header.length()); + if(content.length()) + sendContent(content); +} + +void WebServer::send_P(int code, PGM_P content_type, PGM_P content) { + size_t contentLength = 0; + + if (content != NULL) { + contentLength = strlen_P(content); + } + + String header; + char type[64]; + memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); + _prepareHeader(header, code, (const char* )type, contentLength); + _currentClient.write(header.c_str(), header.length()); + sendContent_P(content); +} + +void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { + String header; + char type[64]; + memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); + _prepareHeader(header, code, (const char* )type, contentLength); + sendContent(header); + sendContent_P(content, contentLength); +} + +void WebServer::send(int code, char* content_type, const String& content) { + send(code, (const char*)content_type, content); +} + +void WebServer::send(int code, const String& content_type, const String& content) { + send(code, (const char*)content_type.c_str(), content); +} + +void WebServer::sendContent(const String& content) { + const char * footer = "\r\n"; + size_t len = content.length(); + if(_chunked) { + char * chunkSize = (char *)malloc(11); + if(chunkSize){ + sprintf(chunkSize, "%x%s", len, footer); + _currentClient.write(chunkSize, strlen(chunkSize)); + free(chunkSize); + } + } + _currentClient.write(content.c_str(), len); + if(_chunked){ + _currentClient.write(footer, 2); + } +} + +void WebServer::sendContent_P(PGM_P content) { + sendContent_P(content, strlen_P(content)); +} + +void WebServer::sendContent_P(PGM_P content, size_t size) { + const char * footer = "\r\n"; + if(_chunked) { + char * chunkSize = (char *)malloc(11); + if(chunkSize){ + sprintf(chunkSize, "%x%s", size, footer); + _currentClient.write(chunkSize, strlen(chunkSize)); + free(chunkSize); + } + } + _currentClient.write(content, size); + if(_chunked){ + _currentClient.write(footer, 2); + } +} + + +String WebServer::arg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if ( _currentArgs[i].key == name ) + return _currentArgs[i].value; + } + return String(); +} + +String WebServer::arg(int i) { + if (i < _currentArgCount) + return _currentArgs[i].value; + return String(); +} + +String WebServer::argName(int i) { + if (i < _currentArgCount) + return _currentArgs[i].key; + return String(); +} + +int WebServer::args() { + return _currentArgCount; +} + +bool WebServer::hasArg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return true; + } + return false; +} + + +String WebServer::header(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if (_currentHeaders[i].key.equalsIgnoreCase(name)) + return _currentHeaders[i].value; + } + return String(); +} + +void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { + _headerKeysCount = headerKeysCount + 1; + if (_currentHeaders) + delete[]_currentHeaders; + _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders[0].key = AUTHORIZATION_HEADER; + for (int i = 1; i < _headerKeysCount; i++){ + _currentHeaders[i].key = headerKeys[i-1]; + } +} + +String WebServer::header(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].value; + return String(); +} + +String WebServer::headerName(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].key; + return String(); +} + +int WebServer::headers() { + return _headerKeysCount; +} + +bool WebServer::hasHeader(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) + return true; + } + return false; +} + +String WebServer::hostHeader() { + return _hostHeader; +} + +void WebServer::onFileUpload(THandlerFunction fn) { + _fileUploadHandler = fn; +} + +void WebServer::onNotFound(THandlerFunction fn) { + _notFoundHandler = fn; +} + +void WebServer::_handleRequest() { + bool handled = false; + if (!_currentHandler){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("request handler not found"); +#endif + } + else { + handled = _currentHandler->handle(*this, _currentMethod, _currentUri); +#ifdef DEBUG_ESP_HTTP_SERVER + if (!handled) { + DEBUG_OUTPUT.println("request handler failed to handle request"); + } +#endif + } + + if (!handled) { + if(_notFoundHandler) { + _notFoundHandler(); + } + else { + send(404, "text/plain", String("Not found: ") + _currentUri); + } + } + + _currentUri = String(); +} + +String WebServer::_responseCodeToString(int code) { + switch (code) { + case 100: return F("Continue"); + case 101: return F("Switching Protocols"); + case 200: return F("OK"); + case 201: return F("Created"); + case 202: return F("Accepted"); + case 203: return F("Non-Authoritative Information"); + case 204: return F("No Content"); + case 205: return F("Reset Content"); + case 206: return F("Partial Content"); + case 300: return F("Multiple Choices"); + case 301: return F("Moved Permanently"); + case 302: return F("Found"); + case 303: return F("See Other"); + case 304: return F("Not Modified"); + case 305: return F("Use Proxy"); + case 307: return F("Temporary Redirect"); + case 400: return F("Bad Request"); + case 401: return F("Unauthorized"); + case 402: return F("Payment Required"); + case 403: return F("Forbidden"); + case 404: return F("Not Found"); + case 405: return F("Method Not Allowed"); + case 406: return F("Not Acceptable"); + case 407: return F("Proxy Authentication Required"); + case 408: return F("Request Time-out"); + case 409: return F("Conflict"); + case 410: return F("Gone"); + case 411: return F("Length Required"); + case 412: return F("Precondition Failed"); + case 413: return F("Request Entity Too Large"); + case 414: return F("Request-URI Too Large"); + case 415: return F("Unsupported Media Type"); + case 416: return F("Requested range not satisfiable"); + case 417: return F("Expectation Failed"); + case 500: return F("Internal Server Error"); + case 501: return F("Not Implemented"); + case 502: return F("Bad Gateway"); + case 503: return F("Service Unavailable"); + case 504: return F("Gateway Time-out"); + case 505: return F("HTTP Version not supported"); + default: return ""; + } +} diff --git a/examples/Advanced/WIFI/WiFiSetting/WebServer.h b/examples/Advanced/WIFI/WiFiSetting/WebServer.h new file mode 100644 index 0000000..2f4cc52 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/WebServer.h @@ -0,0 +1,186 @@ +/* + WebServer.h - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + + +#ifndef WEBSERVER_H +#define WEBSERVER_H + +#include +#include +#include +#include + +enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; +enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, + UPLOAD_FILE_ABORTED }; +enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; + +#define HTTP_DOWNLOAD_UNIT_SIZE 1460 +#define HTTP_UPLOAD_BUFLEN 2048 +#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request +#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive +#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed +#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection + +#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) +#define CONTENT_LENGTH_NOT_SET ((size_t) -2) + +class WebServer; + +typedef struct { + HTTPUploadStatus status; + String filename; + String name; + String type; + size_t totalSize; // file size + size_t currentSize; // size of data currently in buf + uint8_t buf[HTTP_UPLOAD_BUFLEN]; +} HTTPUpload; + +#include "detail/RequestHandler.h" + +namespace fs { +class FS; +} + +class WebServer +{ +public: + WebServer(IPAddress addr, int port = 80); + WebServer(int port = 80); + ~WebServer(); + + void begin(); + void handleClient(); + + void close(); + void stop(); + + bool authenticate(const char * username, const char * password); + void requestAuthentication(); + + typedef std::function THandlerFunction; + void on(const String &uri, THandlerFunction handler); + void on(const String &uri, HTTPMethod method, THandlerFunction fn); + void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void addHandler(RequestHandler* handler); + void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); + void onNotFound(THandlerFunction fn); //called when handler is not assigned + void onFileUpload(THandlerFunction fn); //handle file uploads + + String uri() { return _currentUri; } + HTTPMethod method() { return _currentMethod; } + WiFiClient client() { return _currentClient; } + HTTPUpload& upload() { return _currentUpload; } + + String arg(String name); // get request argument value by name + String arg(int i); // get request argument value by number + String argName(int i); // get request argument name by number + int args(); // get arguments count + bool hasArg(String name); // check if argument exists + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + String header(String name); // get request header value by name + String header(int i); // get request header value by number + String headerName(int i); // get request header name by number + int headers(); // get header count + bool hasHeader(String name); // check if header exists + + String hostHeader(); // get request host header if available or empty String if not + + // send response to the client + // code - HTTP response code, can be 200 or 404 + // content_type - HTTP content type, like "text/plain" or "image/png" + // content - actual content body + void send(int code, const char* content_type = NULL, const String& content = String("")); + void send(int code, char* content_type, const String& content); + void send(int code, const String& content_type, const String& content); + void send_P(int code, PGM_P content_type, PGM_P content); + void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); + + void setContentLength(size_t contentLength); + void sendHeader(const String& name, const String& value, bool first = false); + void sendContent(const String& content); + void sendContent_P(PGM_P content); + void sendContent_P(PGM_P content, size_t size); + + static String urlDecode(const String& text); + +template size_t streamFile(T &file, const String& contentType){ + setContentLength(file.size()); + if (String(file.name()).endsWith(".gz") && + contentType != "application/x-gzip" && + contentType != "application/octet-stream"){ + sendHeader("Content-Encoding", "gzip"); + } + send(200, contentType, ""); + return _currentClient.write(file); +} + +protected: + void _addRequestHandler(RequestHandler* handler); + void _handleRequest(); + bool _parseRequest(WiFiClient& client); + void _parseArguments(String data); + static String _responseCodeToString(int code); + bool _parseForm(WiFiClient& client, String boundary, uint32_t len); + bool _parseFormUploadAborted(); + void _uploadWriteByte(uint8_t b); + uint8_t _uploadReadByte(WiFiClient& client); + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + bool _collectHeader(const char* headerName, const char* headerValue); + + struct RequestArgument { + String key; + String value; + }; + + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + uint8_t _currentVersion; + HTTPClientStatus _currentStatus; + unsigned long _statusChange; + + RequestHandler* _currentHandler; + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + THandlerFunction _notFoundHandler; + THandlerFunction _fileUploadHandler; + + int _currentArgCount; + RequestArgument* _currentArgs; + HTTPUpload _currentUpload; + + int _headerKeysCount; + RequestArgument* _currentHeaders; + size_t _contentLength; + String _responseHeaders; + + String _hostHeader; + bool _chunked; + +}; + + +#endif //WebServer_H diff --git a/examples/Advanced/WIFI/WiFiSetting/WiFiSetting.ino b/examples/Advanced/WIFI/WiFiSetting/WiFiSetting.ino new file mode 100644 index 0000000..8d09631 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/WiFiSetting.ino @@ -0,0 +1,216 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:WiFi connect. wifi连接 +* date:2021/7/27 +******************************************************************************* +*/ +#include > +#include +#include +#include +#include "WebServer.h" +#include + +boolean restoreConfig(); +boolean checkConnection(); +void startWebServer(); +void setupMode(); +String makePage(String title, String contents); +String urlDecode(String input); + +const IPAddress apIP(192, 168, 4, 1); //Define the address of the wireless AP. 定义无线AP的地址 +const char* apSSID = "M5STACK_SETUP"; //Define the name of the created hotspot. 定义创建热点的名称 +boolean settingMode; +String ssidList; +String wifi_ssid; //Store the name of the wireless network. 存储无线网络的名称 +String wifi_password; //Store the password of the wireless network. 存储无线网络的密码 + +// DNSServer dnsServer;. webServer的类 +WebServer webServer(80); + +// wifi config store. wifi配置存储的类 +Preferences preferences; + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色 + M5.Lcd.setRotation(3); + preferences.begin("wifi-config"); + delay(10); + if (restoreConfig()) { //Check if wifi configuration information has been stored. 检测是否已存储wifi配置信息 + if (checkConnection()) { //Check wifi connection. 检测wifi连接情况 + settingMode = false; //Turn off setting mode. 关闭设置模式 + startWebServer(); //Turn on network service. 开启网络服务 + return; //Exit setup(). 退出setup() + } + } + settingMode = true; //If there is no stored wifi configuration information, turn on the setting mode. 若没有已存储的wifi配置信息,则开启设置模式 + setupMode(); +} + +void loop() { + if (settingMode) { + } + webServer.handleClient(); //Check that there is no facility to send requests to the M5StickC Web server over the network. 检查有没有设备通过网络向M5StickC网络服务器发送请求 +} + +boolean restoreConfig() { /* Check whether there is wifi configuration information storage, if there is 1 return, if no return 0. + 检测是否有wifi配置信息存储,若有返回1,无返回0 */ + wifi_ssid = preferences.getString("WIFI_SSID"); + wifi_password = preferences.getString("WIFI_PASSWD"); + M5.Lcd.printf("WIFI-SSID: %s\n",wifi_ssid); //Screen print format string. 屏幕打印格式化字符串 + M5.Lcd.printf("WIFI-PASSWD: %s\n",wifi_password); + WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str()); + + if(wifi_ssid.length() > 0) { + return true; +} else { + return false; + } +} + +boolean checkConnection() { //Check wifi connection. 检测wifi连接情况 + int count = 0; //count. 计数 + M5.Lcd.print("Waiting for Wi-Fi connection"); + while ( count < 30 ) { /* If you fail to connect to wifi within 30*350ms (10.5s), return false; otherwise return true. + 若在30*500ms(15s)内未能连上wifi,返回false;否则返回true */ + if (WiFi.status() == WL_CONNECTED) { + M5.Lcd.printf("\nConnected!\n"); + return (true); + } + delay(350); + M5.Lcd.print("."); + count++; + } + M5.Lcd.println("Timed out."); + return false; +} + +void startWebServer() { //Open the web service. 打开Web服务 + if (settingMode) { //If the setting mode is on. 如果设置模式处于开启状态 + M5.Lcd.print("Starting Web Server at: \n"); + M5.Lcd.print(WiFi.softAPIP()); //Output AP address (you can change the address you want through apIP at the beginning). 输出AP地址(可通过开头的apIP更改自己想要的地址) + webServer.on("/settings", []() { //AP web interface settings. AP网页界面设置 + String s = "

Wi-Fi Settings

Please enter your password by selecting the SSID.

"; + s += "

Password:
"; + webServer.send(200, "text/html", makePage("Wi-Fi Settings", s)); + }); + webServer.on("/setap", []() { + String ssid = urlDecode(webServer.arg("ssid")); + M5.Lcd.printf("SSID: %s\n",ssid); + String pass = urlDecode(webServer.arg("pass")); + M5.Lcd.printf("Password: %s\nWriting SSID to EEPROM...\n",pass); + + // Store wifi config. 存储wifi配置信息 + M5.Lcd.println("Writing Password to nvr..."); + preferences.putString("WIFI_SSID", ssid); + preferences.putString("WIFI_PASSWD", pass); + + M5.Lcd.println("Write nvr done!"); + String s = "

Setup complete.

device will be connected to \""; + s += ssid; + s += "\" after the restart."; + webServer.send(200, "text/html", makePage("Wi-Fi Settings", s)); + delay(2000); + ESP.restart(); //Restart MPU. 重启MPU + }); + webServer.onNotFound([]() { + String s = "

AP mode

Wi-Fi Settings

"; + webServer.send(200, "text/html", makePage("AP mode", s)); + }); + } + else { //If the setting mode is off. 如果设置模式处于关闭状态 + M5.Lcd.print("Starting Web Server at \n"); + M5.Lcd.println(WiFi.localIP()); + webServer.on("/", []() { //AP web interface settings. AP网页界面设置 + String s = "

STA mode

Reset Wi-Fi Settings

"; + webServer.send(200, "text/html", makePage("STA mode", s)); + }); + webServer.on("/reset", []() { + // reset the wifi config + preferences.remove("WIFI_SSID"); + preferences.remove("WIFI_PASSWD"); + String s = "

Wi-Fi settings was reset.

Please reset device.

"; + webServer.send(200, "text/html", makePage("Reset Wi-Fi Settings", s)); + delay(2000); + ESP.restart(); + }); + } + webServer.begin(); //Start web service. 开启web服务 +} + +void setupMode() { + WiFi.mode(WIFI_MODE_STA); //Set Wi-Fi mode to WIFI_MODE_STA. 设置Wi-Fi模式为WIFI_MODE_STA + WiFi.disconnect(); //Disconnect wifi connection. 断开wifi连接 + delay(100); + int n = WiFi.scanNetworks(); //Store the number of wifi scanned into n. 将扫描到的wifi个数存储到n中 + delay(100); + M5.Lcd.println(""); + for (int i = 0; i < n; ++i) { //Save each wifi name scanned to ssidList. 将扫描到的每个wifi名称保存到ssidList中 + ssidList += ""; + } + delay(100); + WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); + WiFi.softAP(apSSID); //Turn on Ap mode. 开启Ap模式 + WiFi.mode(WIFI_MODE_AP); //Set WiFi to soft-AP mode. 设置WiFi为soft-AP模式 + startWebServer(); //Open the web service. 打开Web服务 + M5.Lcd.printf("\nStarting Access Point at \n\"%s\"",apSSID); +} + +String makePage(String title, String contents) { + String s = ""; + s += ""; + s += ""; + s += title; + s += ""; + s += contents; + s += ""; + return s; +} + +String urlDecode(String input) { + String s = input; + s.replace("%20", " "); + s.replace("+", " "); + s.replace("%21", "!"); + s.replace("%22", "\""); + s.replace("%23", "#"); + s.replace("%24", "$"); + s.replace("%25", "%"); + s.replace("%26", "&"); + s.replace("%27", "\'"); + s.replace("%28", "("); + s.replace("%29", ")"); + s.replace("%30", "*"); + s.replace("%31", "+"); + s.replace("%2C", ","); + s.replace("%2E", "."); + s.replace("%2F", "/"); + s.replace("%2C", ","); + s.replace("%3A", ":"); + s.replace("%3A", ";"); + s.replace("%3C", "<"); + s.replace("%3D", "="); + s.replace("%3E", ">"); + s.replace("%3F", "?"); + s.replace("%40", "@"); + s.replace("%5B", "["); + s.replace("%5C", "\\"); + s.replace("%5D", "]"); + s.replace("%5E", "^"); + s.replace("%5F", "-"); + s.replace("%60", "`"); + return s; +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandler.h b/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandler.h new file mode 100644 index 0000000..c1cc909 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandler.h @@ -0,0 +1,19 @@ +#ifndef REQUESTHANDLER_H +#define REQUESTHANDLER_H + +class RequestHandler { +public: + virtual ~RequestHandler() { } + virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; } + virtual bool canUpload(String uri) { (void) uri; return false; } + virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } + virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } + + RequestHandler* next() { return _next; } + void next(RequestHandler* r) { _next = r; } + +private: + RequestHandler* _next = nullptr; +}; + +#endif //REQUESTHANDLER_H diff --git a/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandlersImpl.h b/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandlersImpl.h new file mode 100644 index 0000000..04bcd05 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSetting/detail/RequestHandlersImpl.h @@ -0,0 +1,153 @@ +#ifndef REQUESTHANDLERSIMPL_H +#define REQUESTHANDLERSIMPL_H + +#include "RequestHandler.h" + +class FunctionRequestHandler : public RequestHandler { +public: + FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method) + : _fn(fn) + , _ufn(ufn) + , _uri(uri) + , _method(method) + { + } + + bool canHandle(HTTPMethod requestMethod, String requestUri) override { + if (_method != HTTP_ANY && _method != requestMethod) + return false; + + if (requestUri != _uri) + return false; + + return true; + } + + bool canUpload(String requestUri) override { + if (!_ufn || !canHandle(HTTP_POST, requestUri)) + return false; + + return true; + } + + bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override { + (void) server; + if (!canHandle(requestMethod, requestUri)) + return false; + + _fn(); + return true; + } + + void upload(WebServer& server, String requestUri, HTTPUpload& upload) override { + (void) server; + (void) upload; + if (canUpload(requestUri)) + _ufn(); + } + +protected: + WebServer::THandlerFunction _fn; + WebServer::THandlerFunction _ufn; + String _uri; + HTTPMethod _method; +}; + +class StaticRequestHandler : public RequestHandler { +public: + StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) + : _fs(fs) + , _uri(uri) + , _path(path) + , _cache_header(cache_header) + { + _isFile = fs.exists(path); + // DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header); + _baseUriLength = _uri.length(); + } + + bool canHandle(HTTPMethod requestMethod, String requestUri) override { + if (requestMethod != HTTP_GET) + return false; + + if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) + return false; + + return true; + } + + bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (!canHandle(requestMethod, requestUri)) + return false; + + // DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); + + String path(_path); + + if (!_isFile) { + // Base URI doesn't point to a file. + // If a directory is requested, look for index file. + if (requestUri.endsWith("/")) requestUri += "index.htm"; + + // Append whatever follows this URI in request to get the file path. + path += requestUri.substring(_baseUriLength); + } + // DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); + + String contentType = getContentType(path); + + // look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for + // if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc... + if (!path.endsWith(".gz") && !_fs.exists(path)) { + String pathWithGz = path + ".gz"; + if(_fs.exists(pathWithGz)) + path += ".gz"; + } + + File f = _fs.open(path, "r"); + if (!f) + return false; + + if (_cache_header.length() != 0) + server.sendHeader("Cache-Control", _cache_header); + + server.streamFile(f, contentType); + return true; + } + + static String getContentType(const String& path) { + if (path.endsWith(".html")) return "text/html"; + else if (path.endsWith(".htm")) return "text/html"; + else if (path.endsWith(".css")) return "text/css"; + else if (path.endsWith(".txt")) return "text/plain"; + else if (path.endsWith(".js")) return "application/javascript"; + else if (path.endsWith(".png")) return "image/png"; + else if (path.endsWith(".gif")) return "image/gif"; + else if (path.endsWith(".jpg")) return "image/jpeg"; + else if (path.endsWith(".ico")) return "image/x-icon"; + else if (path.endsWith(".svg")) return "image/svg+xml"; + else if (path.endsWith(".ttf")) return "application/x-font-ttf"; + else if (path.endsWith(".otf")) return "application/x-font-opentype"; + else if (path.endsWith(".woff")) return "application/font-woff"; + else if (path.endsWith(".woff2")) return "application/font-woff2"; + else if (path.endsWith(".eot")) return "application/vnd.ms-fontobject"; + else if (path.endsWith(".sfnt")) return "application/font-sfnt"; + else if (path.endsWith(".xml")) return "text/xml"; + else if (path.endsWith(".pdf")) return "application/pdf"; + else if (path.endsWith(".zip")) return "application/zip"; + else if(path.endsWith(".gz")) return "application/x-gzip"; + else if (path.endsWith(".appcache")) return "text/cache-manifest"; + return "application/octet-stream"; + } + +protected: + FS _fs; + String _uri; + String _path; + String _cache_header; + bool _isFile; + size_t _baseUriLength; +}; + + +#endif //REQUESTHANDLERSIMPL_H diff --git a/examples/Advanced/WIFI/WiFiSmartConfig/WiFiSmartConfig.ino b/examples/Advanced/WIFI/WiFiSmartConfig/WiFiSmartConfig.ino new file mode 100644 index 0000000..8d37d87 --- /dev/null +++ b/examples/Advanced/WIFI/WiFiSmartConfig/WiFiSmartConfig.ino @@ -0,0 +1,51 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:WIFI Smart Config. WIFI智能配网 +* date:2021/7/30 +******************************************************************************* + * Fill in WIFI configuration information through mobile APP to connect M5Stick-C to relevant WIFI + * 通过手机APP填写WIFI配置信息使 M5Stick-C连接至相关WIFI + * APP Download Address: https://www.espressif.com/en/products/software/esp-touch/resources + * APP下载地址: https://www.espressif.com/zh-hans/products/software/esp-touch/resources + */ +#include +#include "WiFi.h" + +void setup() { + M5.begin(); //Init M5Stick-C. 初始化 M5Stick-C + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + WiFi.mode(WIFI_AP_STA); // Set the wifi mode to the mode compatible with the AP and Station, and start intelligent network configuration + WiFi.beginSmartConfig(); // 设置wifi模式为AP 与 Station 兼容模式,并开始智能配网 + + //Wait for the M5Stick-C to receive network information from the phone + //等待M5Stick-C接收到来自手机的配网信息 + M5.Lcd.print("\nWaiting for Phone SmartConfig."); //Screen print format string. 屏幕打印格式化字符串 + while (!WiFi.smartConfigDone()) { //If the smart network is not completed. 若智能配网没有完成 + delay(500); + M5.Lcd.print("."); + } + M5.Lcd.println("\nSmartConfig received."); + + M5.Lcd.println("Waiting for WiFi"); + while (WiFi.status() != WL_CONNECTED) { //M5Stick-C will connect automatically upon receipt of the configuration information, and return true if the connection is successful. 收到配网信息后M5Stick-C将自动连接,若连接成功将返回true + delay(500); + M5.Lcd.print("."); + } + M5.Lcd.fillScreen(BLACK); + M5.lcd.setCursor(0,0); + M5.Lcd.print("\nWiFi Connect To: "); + M5.Lcd.println(WiFi.SSID()); //Output Network name. 输出网络名称 + M5.Lcd.print("IP address: "); + M5.Lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址 + M5.Lcd.print("RSSI: "); + M5.Lcd.println(WiFi.RSSI()); //Output signal strength. 输出信号强度 +} + +void loop() { +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/WiFiTCP/WiFiTCP.ino b/examples/Advanced/WIFI/WiFiTCP/WiFiTCP.ino new file mode 100644 index 0000000..3cc4edb --- /dev/null +++ b/examples/Advanced/WIFI/WiFiTCP/WiFiTCP.ino @@ -0,0 +1,80 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:WIFI TCP. +* date:2021/7/29 +******************************************************************************* + M5Stick-C will sends a message to a TCP server + M5Stick-C 将向TCP服务器发送一条数据 +*/ + +#include +#include +#include + +// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码 +const char *ssid = "cam"; +const char *password = "12345678"; +WiFiMulti WiFiMulti; + +void setup() +{ + int sum=0; + M5.begin(); //Init M5Stick-C. 初始化M5Stick-C + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + WiFiMulti.addAP(ssid, password); //Add wifi configuration information. 添加wifi配置信息 + M5.lcd.printf("Waiting connect to WiFi: %s ...",ssid); //Serial port output format string. 串口输出格式化字符串 + while(WiFiMulti.run() != WL_CONNECTED) { //If the connection to wifi is not established successfully. 如果没有与wifi成功建立连接 + M5.lcd.print("."); + delay(1000); + sum+=1; + if(sum==8) M5.lcd.print("Conncet failed!"); + } + M5.lcd.println("\nWiFi connected"); + M5.lcd.print("IP address: "); + M5.lcd.println(WiFi.localIP()); //The serial port outputs the IP address of the M5Stick-C. 串口输出M5Stick-C的IP地址 + delay(500); +} + +void loop() +{ + M5.lcd.setCursor(0,40); + const char * host = "www.baidu.com"; //Set the IP address or DNS of the TCP server. 设置TCP服务器的ip或dns + const uint16_t port = 80; //The port of the TCP server is specified. 设置TCP服务器的端口 + M5.lcd.printf("Connecting to: %s\n",host); + + WiFiClient client; + if (!client.connect(host, port)) { //Connect to the server. 0 is returned if the connection fails. 连接服务器,若连接失败返回0 + M5.lcd.print("Connection failed.\nWaiting 5 seconds before retrying...\n"); + delay(5000); + return; + } + //send an arbitrary string to the server. 发送一个字符串到上边连接的服务器 + client.print("Send this data to the server"); + //send a basic document request to the server. 向服务器发送一个基本的文档请求. + client.print("GET /index.html HTTP/1.1\n\n"); + int maxloops = 0; + + //wait for the server's reply to become available + //等待服务器的回复 + while (!client.available() && maxloops < 1000){ + maxloops++; + delay(1); //delay 1 msec + } + if (client.available() > 0){ //Detects whether data is received. 检测是否接收到数据 + String line = client.readStringUntil('\r'); //Read information from data received by the device until \r is read. 从设备接收到的数据中读取信息,直至读取到\r时 + M5.lcd.println(line); //String received by serial port output. 串口输出接收到的字符串 + }else{ + M5.lcd.println("client.available() timed out "); + } + M5.lcd.println("Closing connection."); + client.stop(); + M5.lcd.println("Waiting 5 seconds before restarting..."); + delay(5000); + M5.lcd.fillRect(0,40,320,220,BLACK); +} \ No newline at end of file diff --git a/examples/Advanced/WIFI/mDNS_Find/mDNS_Find.ino b/examples/Advanced/WIFI/mDNS_Find/mDNS_Find.ino new file mode 100644 index 0000000..51e0c67 --- /dev/null +++ b/examples/Advanced/WIFI/mDNS_Find/mDNS_Find.ino @@ -0,0 +1,86 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:announcing & finding services. 广播&查找服务 +* date:2021/8/3 +******************************************************************************* +*/ + +#include +#include +#include + +// Set the name and password of the wifi to be connected. 配置所连接wifi的名称和密码 +const char* ssid = "M5"; +const char* password = "123455"; + +void browseService(const char * service, const char * proto){ //find devices. 查找设备 + M5.Lcd.printf("Browsing for _%s._%s.local ", service, proto); + int n = MDNS.queryService(service, proto); //Store the number of devices found in n. 将找到的设备数存储在n中 + if (n == 0) { //if don't have any devices. 如果没有任何设备 + M5.Lcd.println("no services found"); + } else { + M5.Lcd.printf("%d service(s) found\n",n); + for (int i = 0; i < n; ++i) { // Print details for each service found. 打印每个找到的设备 + M5.Lcd.printf(" %d: ",i + 1); + M5.Lcd.print(MDNS.hostname(i)); //output the devies name. 输出设备名称 + M5.Lcd.print(" "); + M5.Lcd.print(MDNS.IP(i)); //Output the devices IP Address. 输出设备的IP地址 + M5.Lcd.printf(":%d\n",MDNS.port(i)); //output the devices port. 输出设备的端口号 + } + } + M5.Lcd.println(); +} + +void setup() { + M5.begin(); //Init M5Stick. 初始化 M5Stick + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + WiFi.begin(ssid, password); //Connect wifi and return connection status. 连接wifi并返回连接状态 + M5.Lcd.print("Connected to "); + M5.Lcd.print(ssid); //Output Network name. 输出网络名称 + while (WiFi.status() != WL_CONNECTED) { //If the wifi connection fails. 若wifi未连接成功 + delay(500); //delay 0.5s. 延迟0.5s + M5.Lcd.print("."); + } + M5.Lcd.print("Success!\nIP address: "); + M5.Lcd.println(WiFi.localIP()); //Output IP Address. 输出IP地址 + + if (!MDNS.begin("ESP32_Browser")) { //if init error. 如果初始化错误 + M5.Lcd.println("Error setting up MDNS responder!"); + while(1){ + delay(1000); + } + } +} + +void loop() { + browseService("http", "tcp"); + delay(1000); + M5.Lcd.fillRect(0,0,160,80,BLACK); + M5.Lcd.setCursor(0,0); + browseService("arduino", "tcp"); + delay(1000); + browseService("workstation", "tcp"); + delay(3000); + M5.Lcd.fillRect(0,0,160,80,BLACK); + M5.Lcd.setCursor(0,0); + browseService("smb", "tcp"); + delay(1000); + browseService("afpovertcp", "tcp"); + delay(1000); + M5.Lcd.fillRect(0,0,160,80,BLACK); + M5.Lcd.setCursor(0,0); + browseService("ftp", "tcp"); + delay(1000); + browseService("ipp", "tcp"); + delay(1000); + M5.Lcd.fillRect(0,0,160,80,BLACK); + M5.Lcd.setCursor(0,0); + browseService("printer", "tcp"); + delay(10000); +} \ No newline at end of file diff --git a/examples/Basics/FactoryTest/FactoryTest.ino b/examples/Basics/FactoryTest/FactoryTest.ino index 9b9550d..b852c81 100644 --- a/examples/Basics/FactoryTest/FactoryTest.ino +++ b/examples/Basics/FactoryTest/FactoryTest.ino @@ -1356,7 +1356,7 @@ void setup() Displaybuff(); delay(30); } - //Serial.printf("FUCK STC\r\n"); + //Serial.printf(" STC\r\n"); if (InitI2SMicroPhone() != true) { ErrorMeg(0x51, "MicroPhone error",0 ,true); diff --git a/examples/Unit/4-RELAY/4-RELAY.ino b/examples/Unit/4-RELAY/4-RELAY.ino index 903a780..e101e4e 100644 --- a/examples/Unit/4-RELAY/4-RELAY.ino +++ b/examples/Unit/4-RELAY/4-RELAY.ino @@ -1,123 +1,69 @@ /* - Description: Control 4 relays and demonstrate the asynchronous control relay LED -*/ +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:4-Relay. 4个继电器 +* date:2021/8/10 +******************************************************************************* + Please connect to port,Control 4 relays and demonstrate the asynchronous control relay LED + 请连接端口,控制4继电器,并演示异步控制继电器LED +------------------------------------------------------------------------------- + RELAY control reg | 0x10 + ----------------------------------------------------------------------------- + Relay_ctrl_mode_reg[0] | R/W | System control + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + | R | R | R | R | R | R | R | Sync Mode | + | -Sync Mode:0 LED&Relay Async + | -Sync Mode:1 LED&Relay Sync + --------------------------------------------------------------------------------- + Relay_ctrl_mode_reg[1] | R/W | Relay & LED control + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + | LED1| LED2| LED3| LED4| RLY1| RLY2| RLY3| RLY4| +-------------------------------------------------------------------------------*/ #include - -/*-----------------------------------------------------------------------------*/ -// |RELAY control reg | 0x10 -// |----------------------------------------------------------------------------- -// |Relay_ctrl_mode_reg[0] | R/W | System control -// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | -// | R | R | R | R | R | R | R | Sync Mode | -// | -Sync Mode:0 LED&Relay Async -// | -Sync Mode:1 LED&Relay Sync -//--------------------------------------------------------------------------------- -// |Relay_ctrl_mode_reg[1] | R/W | Relay & LED control -// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | -// | LED1| LED2| LED3| LED4| RLY1| RLY2| RLY3| RLY4| -// -/*-------------------------------------------------------------------------------*/ - -void WriteRelayReg( int regAddr, int data ) -{ - Wire.beginTransmission(0x26); - Wire.write(regAddr); - Wire.write(data); - Wire.endTransmission(); - Serial.printf("[ W ] %02X : %02X. \r\n", regAddr, data); -} - -int readRelayReg(int regAddr) -{ - Wire.beginTransmission(0x26); - Wire.write(regAddr); - Wire.endTransmission(); - Wire.requestFrom(0x26, 1); - int data = Wire.read() & 0x00ff; - Serial.printf("[ R ] %02X : %02X. \r\n", regAddr, data); - return data; -} - -void WriteRelayNumber( int number, int state ) -{ - int StateFromDevice = readRelayReg(0x11); - if ( state == 0 ) - { - StateFromDevice &= ~( 0x01 << number ); - } - else - { - StateFromDevice |= ( 0x01 << number ); - } - WriteRelayReg(0x11, StateFromDevice); -} +#include "UNIT_4RELAY.h" +UNIT_4RELAY unit_4relay; void setup() { - // put your setup code here, to run once: - M5.begin(); - M5.Lcd.setRotation(3); - Wire.begin(32, 33); - M5.Lcd.setCursor(50, 5); - M5.Lcd.setTextColor(TFT_GREEN, TFT_BLACK); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.setCursor(50, 5); //Set the cursor position to (50,5). 将光标位置设置为(50,5) M5.Lcd.print("4-RELAY UNIT"); M5.Lcd.setCursor(0, 25); M5.Lcd.print("Independent Switch:"); - M5.Lcd.setCursor(0, 45); - M5.Lcd.print("LED Sync/Async:"); - readRelayReg(0x10); - readRelayReg(0x11); - WriteRelayReg(0x10, 1); - WriteRelayReg(0x11, 0); - //WriteRelayNumber(0,0); + Wire.begin(32, 33); //Initialize pin 32,33. 初始化32,33引脚 + unit_4relay.Init(1); //Set the lamp and relay to asynchronous mode(Async = 0,Sync = 1). 将灯和继电器设为非同步模式 } -int count_i = 0; -bool flag_led, flag_relay = false; - +char count_i = 1; +bool sync_flag = 0, all_flag= 0; void loop() { - if (M5.BtnA.wasPressed()) - { - M5.Lcd.fillRect(120, 25, 30, 10, TFT_BLACK); - M5.Lcd.setCursor(120, 25); - if (count_i < 4) - { + M5.update(); //Check button down state. 检测按键按下状态 + if(M5.BtnA.wasPressed()){ //If button A is pressed. 如果按键A按下 + M5.Lcd.fillRect(117, 25, 40, 20, BLACK); + M5.Lcd.setCursor(117, 25); + if(count_i<5){ M5.Lcd.printf("%d ON", count_i); - WriteRelayReg(0x11, (0x01 << count_i)); - } - else if (count_i == 4) - { - M5.Lcd.print("ON"); - for (int i = 0; i < 4; i++) - { - WriteRelayNumber(i, 1); - } - } - else if (count_i == 5) - { - M5.Lcd.print("OFF"); - for (int i = 0; i < 4; i++) - { - WriteRelayNumber(i, 0); - } + unit_4relay.relayWrite(count_i-1,1); //Open the relay at Count_i. 打开count_i处的继电器 + unit_4relay.relayWrite(count_i-2,0); + unit_4relay.LEDWrite(count_i-1,1); + unit_4relay.LEDWrite(count_i-2,0); + }else{ + M5.Lcd.printf("ALL.ON "); + unit_4relay.relayALL(1); //Open all the relays. 打开所有的继电器 + unit_4relay.LED_ALL(1); } count_i++; - if ( count_i >= 6 ) count_i = 0; - } - if (M5.BtnB.wasPressed()) - { - M5.Lcd.fillRect(100, 45, 30, 10, TFT_BLACK); - M5.Lcd.setCursor(100, 45); - if (!flag_led) { - M5.Lcd.print("Async"); - WriteRelayReg(0x10, 0); - } else { - M5.Lcd.print("Sync"); - WriteRelayReg(0x10, 1); + if(count_i > 6) { + count_i = 1; + unit_4relay.relayALL(0); //Open all the relays. 打开所有的继电器 + unit_4relay.LED_ALL(0); } - flag_led = !flag_led; } - - M5.update(); -} +} \ No newline at end of file diff --git a/examples/Unit/ADC_ADS1100/ADC_ADS1100.ino b/examples/Unit/ADC_ADS1100/ADC_ADS1100.ino index 6afd87c..55e0f0c 100644 --- a/examples/Unit/ADC_ADS1100/ADC_ADS1100.ino +++ b/examples/Unit/ADC_ADS1100/ADC_ADS1100.ino @@ -1,80 +1,75 @@ /* - hardware: M5Stack unit ADC - connect: PORTA(21, 22) or PORTC(17, 16) +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: ADC. A/D转换器 +* date:2021/8/18 +******************************************************************************* + Please connect to Port,Use ADC Unit to convert 0 ~ 12V analog voltage into 16-bit data and display it on the screen. + 请连接端口,利用ADC单元将0 ~ 12V模拟电压转换成16位数据显示在屏幕上。 */ + #include -#include -#include "ADS1100.h" +#include "M5_ADS1100.h" ADS1100 ads; void setup(void) { - M5.begin(true, false, false); - Serial.begin(115200); - - M5.Lcd.fillScreen(BLACK); - M5.Lcd.setTextColor(ORANGE); - - // The address can be changed making the option of connecting multiple devices - ads.getAddr_ADS1100(ADS1100_DEFAULT_ADDRESS); // 0x48, 1001 000 (ADDR = GND) + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 - // The ADC gain (PGA), Device operating mode, Data rate - // can be changed via the following functions + // The address can be changed making the option of connecting multiple devices + // 地址可以改变,以连接多个设备 + ads.getAddr_ADS1100(ADS1100_DEFAULT_ADDRESS); // 0x48, 1001 000 (ADDR = GND) - ads.setGain(GAIN_ONE); // 1x gain(default) - // ads.setGain(GAIN_TWO); // 2x gain - // ads.setGain(GAIN_FOUR); // 4x gain - // ads.setGain(GAIN_EIGHT); // 8x gain + //The ADC gain (PGA). ADC增益(PGA) + ads.setGain(GAIN_ONE); // 1x gain(default) + // ads.setGain(GAIN_TWO); // 2x gain + // ads.setGain(GAIN_FOUR); // 4x gain + // ads.setGain(GAIN_EIGHT); // 8x gain - ads.setMode(MODE_CONTIN); // Continuous conversion mode (default) - // ads.setMode(MODE_SINGLE); // Single-conversion mode + //Device operating mode. 设备工作模式 + ads.setMode(MODE_CONTIN); // Continuous conversion mode (default) + // ads.setMode(MODE_SINGLE); // Single-conversion mode - ads.setRate(RATE_8); // 8SPS (default) - // ads.setRate(RATE_16); // 16SPS - // ads.setRate(RATE_32); // 32SPS - // ads.setRate(RATE_128); // 128SPS + //Data rate. 数据速率 + ads.setRate(RATE_8); // 8SPS (default) + // ads.setRate(RATE_16); // 16SPS + // ads.setRate(RATE_32); // 32SPS + // ads.setRate(RATE_128); // 128SPS - ads.setOSMode(OSMODE_SINGLE); // Set to start a single-conversion + ads.setOSMode(OSMODE_SINGLE); // Set to start a single-conversion. 设置开始一次转换 - ads.begin(); + ads.begin(); //Sets up the Hardware. 设置硬件 + M5.Lcd.print("ADC"); } void loop(void) { - byte error; - int8_t address; + byte error; + int8_t address; - address = ads.ads_i2cAddress; - // The i2c_scanner uses the return value of - // the Write.endTransmisstion to see if - // a device did acknowledge to the address. - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) - { - int16_t result; - - Serial.println("Getting Differential Reading from ADS1100"); - Serial.println(" "); - result = ads.Measure_Differential(); - Serial.print("Digital Value of Analog Input between Channel 0 and 1: "); - Serial.println(result); - M5.Lcd.fillScreen(BLACK); - char data[20] = { 0 }; - sprintf(data, "%d", result); - M5.Lcd.drawCentreString(data, 40, 80, 4); - Serial.println(" "); - Serial.println(" *************************** "); - Serial.println(" "); - } - else - { - Serial.println("ADS1100 Disconnected!"); - Serial.println(" "); - Serial.println(" ************ "); - Serial.println(" "); - } - - delay(1000); -} + address = ads.ads_i2cAddress; + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (error == 0) //If the device is connected. 如果连接上设备 + { + int16_t result; + result = ads.Measure_Differential(); + M5.Lcd.fillScreen(BLACK); + char data[20] = { 0 }; + sprintf(data, "%d", result); + M5.Lcd.drawCentreString(data, 0, 20, 4); + } + else + { + M5.Lcd.setCursor(0,40); + M5.Lcd.drawString("No Found ADC sensor.",0, 20); + } + delay(1000); +} \ No newline at end of file diff --git a/examples/Unit/ADC_ADS1100/ADS1100.cpp b/examples/Unit/ADC_ADS1100/ADS1100.cpp deleted file mode 100644 index 3087c8d..0000000 --- a/examples/Unit/ADC_ADS1100/ADS1100.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************/ -/* - Distributed with a free-will license. - Use it any way you want, profit or free, provided it fits in the licenses of its associated works. - ADS1100 - This code is designed to work with the ADS1100_I2CADC I2C Mini Module available from ControlEverything.com. - https://www.controleverything.com/content/Analog-Digital-Converters?sku=ADS1100_I2CADC#tabs-0-product_tabset-2 -*/ -/**************************************************************************/ - -#if ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#include - -#include "ADS1100.h" - -/**************************************************************************/ -/* - Abstract away platform differences in Arduino wire library -*/ -/**************************************************************************/ -static uint8_t i2cread(void) -{ - #if ARDUINO >= 100 - return Wire.read(); - #else - return Wire.receive(); - #endif -} - -/**************************************************************************/ -/* - Abstract away platform differences in Arduino wire library -*/ -/**************************************************************************/ -static void i2cwrite(uint8_t x) -{ - #if ARDUINO >= 100 - Wire.write((uint8_t)x); - #else - Wire.send(x); - #endif -} - -/**************************************************************************/ -/* - Writes 8-bits to the destination register -*/ -/**************************************************************************/ -static void writeRegister(uint8_t i2cAddress, uint8_t value) -{ - Wire.beginTransmission(i2cAddress); - i2cwrite((uint8_t)value); - Wire.endTransmission(); -} - -/**************************************************************************/ -/* - Reads 16-bits from the destination register -*/ -/**************************************************************************/ -static uint16_t readRegister(uint8_t i2cAddress) -{ - Wire.beginTransmission(i2cAddress); - Wire.endTransmission(); - Wire.requestFrom(i2cAddress, (uint8_t)2); - return (int16_t)((i2cread() << 8) | i2cread()); -} - -/**************************************************************************/ -/* - Instantiates a new ADS1100 class with appropriate properties -*/ -/**************************************************************************/ -void ADS1100::getAddr_ADS1100(uint8_t i2cAddress) -{ - ads_i2cAddress = i2cAddress; - ads_conversionDelay = ADS1100_CONVERSIONDELAY; -} - -/**************************************************************************/ -/* - Sets up the Hardware -*/ -/**************************************************************************/ -void ADS1100::begin() -{ - Wire.begin(); -} - -/**************************************************************************/ -/* - Sets the Operational status/single-shot conversion start - This determines the operational status of the device -*/ -/**************************************************************************/ -void ADS1100::setOSMode(adsOSMode_t osmode) -{ - ads_osmode = osmode; -} - -/**************************************************************************/ -/* - Gets the Operational status/single-shot conversion start -*/ -/**************************************************************************/ -adsOSMode_t ADS1100::getOSMode() -{ - return ads_osmode; -} - -/**************************************************************************/ -/* - Sets the Device operating mode - This controls the current operational mode of the ADS1100 -*/ -/**************************************************************************/ -void ADS1100::setMode(adsMode_t mode) -{ - ads_mode = mode; -} - -/**************************************************************************/ -/* - Gets the Device operating mode -*/ -/**************************************************************************/ -adsMode_t ADS1100::getMode() -{ - return ads_mode; -} - -/**************************************************************************/ -/* - Sets the Date Rate - This controls the data rate setting -*/ -/**************************************************************************/ -void ADS1100::setRate(adsRate_t rate) -{ - ads_rate = rate; -} - -/**************************************************************************/ -/* - Gets the Date Rate -*/ -/**************************************************************************/ -adsRate_t ADS1100::getRate() -{ - return ads_rate; -} - -/**************************************************************************/ -/* - Sets the gain and input voltage range - This configures the programmable gain amplifier -*/ -/**************************************************************************/ -void ADS1100::setGain(adsGain_t gain) -{ - ads_gain = gain; -} - -/**************************************************************************/ -/* - Gets a gain and input voltage range -*/ -/**************************************************************************/ -adsGain_t ADS1100::getGain() -{ - return ads_gain; -} - -/**************************************************************************/ -/* - Reads the conversion results, measuring the voltage - difference between the P and N input - Generates a signed value since the difference can be either - positive or negative -*/ -/**************************************************************************/ -int16_t ADS1100::Measure_Differential() -{ - // Start with default values - uint16_t config = 0; - - // Set Operational status/single-shot conversion start - config |= ads_osmode; - - // Set Device operating mode - config |= ads_mode; - - // Set Data rate - config |= ads_rate; - - // Set PGA/voltage range - config |= ads_gain; - - // Write config register to the ADC - writeRegister(ads_i2cAddress, config); - - // Wait for the conversion to complete - delay(ads_conversionDelay); - - // Read the conversion results - uint16_t raw_adc = readRegister(ads_i2cAddress); - return (int16_t)raw_adc; -} diff --git a/examples/Unit/ADC_ADS1100/ADS1100.h b/examples/Unit/ADC_ADS1100/ADS1100.h deleted file mode 100644 index 8bf3bc8..0000000 --- a/examples/Unit/ADC_ADS1100/ADS1100.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************/ -/* - Distributed with a free-will license. - Use it any way you want, profit or free, provided it fits in the licenses of its associated works. - ADS1100 - This code is designed to work with the ADS1100_I2CADC I2C Mini Module available from ControlEverything.com. - https://www.controleverything.com/content/Analog-Digital-Converters?sku=ADS1100_I2CADC#tabs-0-product_tabset-2 -*/ -/**************************************************************************/ - -#if ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#include - -/************************************************************************** - I2C ADDRESS/BITS -**************************************************************************/ - #define ADS1100_DEFAULT_ADDRESS (0x48) // 1001 000 (ADDR = GND) - -/************************************************************************** - CONVERSION DELAY (in mS) -**************************************************************************/ - #define ADS1100_CONVERSIONDELAY (100) - -/************************************************************************** - CONFIG REGISTER -**************************************************************************/ - #define ADS1100_REG_CONFIG_OS_MASK (0x80) // Conversion - #define ADS1100_REG_CONFIG_OS_NOEFFECT (0x00) // Write: Bit = 0 No effect - #define ADS1100_REG_CONFIG_OS_SINGLE (0x80) // Write: Bit = 1 Begin a conversion (default) - #define ADS1100_REG_CONFIG_OS_BUSY (0x00) // Read: Bit = 0 Device is not performing a conversion - #define ADS1100_REG_CONFIG_OS_NOTBUSY (0x80) // Read: Bit = 1 Device is busy performing a conversion - - #define ADS1100_REG_CONFIG_MODE_MASK (0x10) // Device operating mode - #define ADS1100_REG_CONFIG_MODE_CONTIN (0x00) // Continuous conversion mode (default) - #define ADS1100_REG_CONFIG_MODE_SINGLE (0x10) // Single-conversion mode - - #define ADS1100_REG_CONFIG_DR_MASK (0x0C) // Data rate - #define ADS1100_REG_CONFIG_DR_128SPS (0x00) // 128 samples per second - #define ADS1100_REG_CONFIG_DR_32SPS (0x04) // 32 samples per second - #define ADS1100_REG_CONFIG_DR_16SPS (0x08) // 16 samples per second - #define ADS1100_REG_CONFIG_DR_8SPS (0x0C) // 8 samples per second (default) - - #define ADS1100_REG_CONFIG_PGA_MASK (0x03) // Programmable gain amplifier configuration - #define ADS1100_REG_CONFIG_PGA_1 (0x00) // Gain 1 (default) - #define ADS1100_REG_CONFIG_PGA_2 (0x01) // Gain 2 - #define ADS1100_REG_CONFIG_PGA_4 (0x02) // Gain 4 - #define ADS1100_REG_CONFIG_PGA_8 (0x03) // Gain 8 - -/**************************************************************************/ - - -typedef enum -{ - OSMODE_SINGLE = ADS1100_REG_CONFIG_OS_SINGLE, - OSMODE_BUSY = ADS1100_REG_CONFIG_OS_BUSY, - OSMODE_NOTBUSY = ADS1100_REG_CONFIG_OS_NOTBUSY - -} adsOSMode_t; - -typedef enum -{ - MODE_CONTIN = ADS1100_REG_CONFIG_MODE_CONTIN, - MODE_SINGLE = ADS1100_REG_CONFIG_MODE_SINGLE -} adsMode_t; - -typedef enum -{ - RATE_1288 = ADS1100_REG_CONFIG_DR_128SPS, - RATE_32 = ADS1100_REG_CONFIG_DR_32SPS, - RATE_16 = ADS1100_REG_CONFIG_DR_16SPS, - RATE_8 = ADS1100_REG_CONFIG_DR_8SPS -} adsRate_t; - -typedef enum -{ - GAIN_ONE = ADS1100_REG_CONFIG_PGA_1, - GAIN_TWO = ADS1100_REG_CONFIG_PGA_2, - GAIN_FOUR = ADS1100_REG_CONFIG_PGA_4, - GAIN_EIGHT = ADS1100_REG_CONFIG_PGA_8 -} adsGain_t; - - -class ADS1100 -{ - protected: - // Instance-specific properties - uint8_t ads_conversionDelay; - adsOSMode_t ads_osmode; - adsMode_t ads_mode; - adsRate_t ads_rate; - adsGain_t ads_gain; - - public: - uint8_t ads_i2cAddress; - void getAddr_ADS1100(uint8_t i2cAddress); - void begin(void); - int16_t Measure_Differential(); - void setOSMode(adsOSMode_t osmode); - adsOSMode_t getOSMode(void); - void setMode(adsMode_t mode); - adsMode_t getMode(void); - void setRate(adsRate_t rate); - adsRate_t getRate(void); - void setGain(adsGain_t gain); - adsGain_t getGain(void); - - private: -}; \ No newline at end of file diff --git a/examples/Unit/AMeter_ADS1115/AMeter_ADS1115.ino b/examples/Unit/AMeter_ADS1115/AMeter_ADS1115.ino index dc76c39..9f8191a 100644 --- a/examples/Unit/AMeter_ADS1115/AMeter_ADS1115.ino +++ b/examples/Unit/AMeter_ADS1115/AMeter_ADS1115.ino @@ -1,12 +1,23 @@ /* - Description: Measure current and display - EEPROM (0x51) has built-in calibration parameters when leaving the factory. - Please do not write to the EEPROM, otherwise the calibration data will be overwritten and the measurement results will be inaccurate. +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Ameter_ADS1115. 电流计 +* date:2021/8/19 +******************************************************************************* + Please connect to Port A,Measure current and display. + 请连接端口A,测量电流并显示到屏幕上 + Pay attention: EEPROM (0x51) has built-in calibration parameters when leaving the factory. + Please do not write to the EEPROM, otherwise the calibration data will be overwritten and the measurement results will be inaccurate. + 注意: EEPROM (0x51)在出厂时具有内置的校准参数。请不要写入EEPROM,否则校准数据会被覆盖,测量结果会不准确。 */ -#include -#include "ammeter.h" #include +#include "M5_ADS1115.h" Ammeter ammeter; @@ -21,7 +32,7 @@ int16_t hope = 0.0; ammeterGain_t now_gain = PAG_512; void setup(void) { - M5.begin(); + M5.begin(); //Init M5StickC. 初始化M5StickC Wire.begin(); ammeter.setMode(SINGLESHOT); @@ -36,23 +47,18 @@ void setup(void) { // | PAG_256 | 8 | M5.Lcd.fillScreen(BLACK); - M5.Lcd.setTextFont(1); + M5.Lcd.setTextFont(1); //Set font to 1 point font. 设置字体为1号字体 - M5.Lcd.setCursor(30, 150); - M5.Lcd.printf("2A"); + M5.Lcd.setCursor(30, 150); //Set the cursor at (30, 150). 将光标设置在(30, 150) + M5.Lcd.printf("2A"); -// M5.Lcd.setCursor(70, 60); -// M5.Lcd.printf("SAVE"); - - // bool result1 = ammeter.saveCalibration2EEPROM(PAG_256, 1024, 1024); - // delay(10); } void loop(void) { - M5.update(); + M5.update(); //Check the status of the key. 检测按键的状态 if (M5.BtnA.wasPressed()) { - ammeter.setMode(SINGLESHOT); - ammeter.setRate(RATE_8); + ammeter.setMode(SINGLESHOT); //Set the mode. 设置模式 + ammeter.setRate(RATE_8); //Set the rate. 设置速率 ammeter.setGain(PAG_512); now_gain = PAG_512; hope = page512_volt / ammeter.resolution; @@ -62,28 +68,8 @@ void loop(void) { } } -// if (M5.BtnB.wasPressed()) { -// bool success = ammeter.saveCalibration2EEPROM(now_gain, hope, adc_raw); -// M5.Lcd.setCursor(70, 60); -// -// if (success) { -// M5.Lcd.setTextColor(GREEN, BLACK); -// } else { -// M5.Lcd.setTextColor(RED, BLACK); -// } -// -// M5.Lcd.printf("SAVE"); -// -// delay(300); -// M5.Lcd.setCursor(70, 60); -// M5.Lcd.setTextColor(WHITE, BLACK); -// M5.Lcd.printf("SAVE"); -// -// ammeter.setGain(now_gain); -// } - float current = ammeter.getCurrent(); - + volt_raw_list[raw_now_ptr] = ammeter.adc_raw; raw_now_ptr = (raw_now_ptr == 9) ? 0 : (raw_now_ptr + 1); @@ -109,12 +95,12 @@ void loop(void) { M5.Lcd.printf("Hope volt:"); M5.Lcd.setCursor(5, 10); M5.Lcd.printf("%.2f mAn", page512_volt); - + M5.Lcd.setCursor(2, 25); M5.Lcd.printf("Hope ADC:"); M5.Lcd.setCursor(5, 35); M5.Lcd.printf("%d", hope); - + M5.Lcd.setTextColor(WHITE, BLACK); M5.Lcd.setCursor(2, 50); M5.Lcd.printf("Cal volt:"); diff --git a/examples/Unit/AMeter_ADS1115/ammeter.cpp b/examples/Unit/AMeter_ADS1115/ammeter.cpp deleted file mode 100644 index cfd01f8..0000000 --- a/examples/Unit/AMeter_ADS1115/ammeter.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "ammeter.h" -#include "Wire.h" - -void Ammeter::i2cBegin() { - // Wire.begin(); -} - -bool Ammeter::i2cReadBytes(uint8_t addr, uint8_t reg_addr, uint8_t* buff, uint16_t len) { - Wire.beginTransmission(addr); - Wire.write(reg_addr); - uint8_t i = 0; - if (Wire.endTransmission(false) == 0 && Wire.requestFrom(addr, (uint8_t)len)) { - while (Wire.available()) { - buff[i++] = Wire.read(); - } - return true; - } - - return false; -} - -bool Ammeter::i2cWriteBytes(uint8_t addr, uint8_t reg_addr, uint8_t* buff, uint16_t len) { - bool function_result = false; - - Wire.beginTransmission(addr); - Wire.write(reg_addr); - for(int i = 0; i < len; i++) { - Wire.write(*(buff+i)); - } - function_result = (Wire.endTransmission() == 0); - return function_result; -} - -bool Ammeter::i2cReadU16(uint8_t addr, uint8_t reg_addr, uint16_t* value) { - uint8_t read_buf[2] = {0x00, 0x00}; - bool result = i2cReadBytes(addr, reg_addr, read_buf, 2); - *value = (read_buf[0] << 8) | read_buf[1]; - return result; -} - -bool Ammeter::i2cWriteU16(uint8_t addr, uint8_t reg_addr, uint16_t value) { - uint8_t write_buf[2]; - write_buf[0] = value >> 8; - write_buf[1] = value & 0xff; - return i2cWriteBytes(addr, reg_addr, write_buf, 2); -} - -float Ammeter::getResolution(ammeterGain_t gain) { - switch (gain) { - case PAG_6144: - return ADS1115_MV_6144 / AMMETER_PRESSURE_COEFFICIENT; - case PAG_4096: - return ADS1115_MV_4096 / AMMETER_PRESSURE_COEFFICIENT; - case PAG_2048: - return ADS1115_MV_2048 / AMMETER_PRESSURE_COEFFICIENT; - case PAG_1024: - return ADS1115_MV_1024 / AMMETER_PRESSURE_COEFFICIENT; - case PAG_512: - return ADS1115_MV_512 / AMMETER_PRESSURE_COEFFICIENT; - case PAG_256: - return ADS1115_MV_256 / AMMETER_PRESSURE_COEFFICIENT; - default: - return ADS1115_MV_256 / AMMETER_PRESSURE_COEFFICIENT; - }; -} - -uint8_t Ammeter::getPGAEEEPROMAddr(ammeterGain_t gain) { - switch (gain) { - case PAG_6144: - return AMMETER_PAG_6144_CAL_ADDR; - case PAG_4096: - return AMMETER_PAG_4096_CAL_ADDR; - case PAG_2048: - return AMMETER_PAG_2048_CAL_ADDR; - case PAG_1024: - return AMMETER_PAG_1024_CAL_ADDR; - case PAG_512: - return AMMETER_PAG_512_CAL_ADDR; - case PAG_256: - return AMMETER_PAG_256_CAL_ADDR; - default: - return 0x00; - }; -} - -uint16_t Ammeter::getCoverTime(ammeterRate_t rate) { - switch (rate) { - case RATE_8: - return 1000 / 8; - case RATE_16: - return 1000 / 16; - case RATE_32: - return 1000 / 32; - case RATE_64: - return 1000 / 64; - case RATE_128: - return 1000 / 128; - case RATE_250: - return 1000 / 250; - case RATE_475: - return 1000 / 475; - case RATE_860: - return 1000 / 860; - default: - return 1000 / 128; - }; -} - -Ammeter::Ammeter(uint8_t ads1115_addr, uint8_t eeprom_addr) { - _ads1115_addr = ads1115_addr; - _eeprom_addr = eeprom_addr; - _gain = PAG_2048; - _mode = SINGLESHOT; - _rate = RATE_128; - calibration_factor = 1; - adc_raw = 0; - resolution = getResolution(_gain); - cover_time = getCoverTime(_rate); -} - -void Ammeter::setGain(ammeterGain_t gain) { - uint16_t reg_value = 0; - bool result = i2cReadU16(_ads1115_addr, ADS1115_RA_CONFIG, ®_value); - - if (result == false) { - return; - } - - reg_value &= ~(0b0111 << 9); - reg_value |= gain << 9; - - result = i2cWriteU16(_ads1115_addr, ADS1115_RA_CONFIG, reg_value); - - if (result) { - _gain = gain; - resolution = getResolution(gain); - int16_t hope = 1; - int16_t actual = 1; - if (readCalibrationFromEEPROM(gain, &hope, &actual)) { - calibration_factor = fabs((double)hope / actual); - } - } -} - -void Ammeter::setRate(ammeterRate_t rate) { - uint16_t reg_value = 0; - bool result = i2cReadU16(_ads1115_addr, ADS1115_RA_CONFIG, ®_value); - if (result == false) { - return; - } - - reg_value &= ~(0b0111 << 5); - reg_value |= rate << 5; - - result = i2cWriteU16(_ads1115_addr, ADS1115_RA_CONFIG, reg_value); - - if (result) { - _rate = rate; - cover_time = getCoverTime(_rate); - } - - return; -} - -void Ammeter::setMode(ammeterMode_t mode) { - uint16_t reg_value = 0; - bool result = i2cReadU16(_ads1115_addr, ADS1115_RA_CONFIG, ®_value); - if (result == false) { - return; - } - - reg_value &= ~(0b0001 << 8); - reg_value |= mode << 8; - - result = i2cWriteU16(_ads1115_addr, ADS1115_RA_CONFIG, reg_value); - if (result) { - _mode = mode; - } - - return; -} - -bool Ammeter::isInConversion() { - uint16_t value = 0x00; - i2cReadU16(_ads1115_addr, ADS1115_RA_CONFIG, &value); - - return (value & (1 << 15)) ? false : true; -} - -void Ammeter::startSingleConversion() { - uint16_t reg_value = 0; - bool result = i2cReadU16(_ads1115_addr, ADS1115_RA_CONFIG, ®_value); - - if (result == false) { - return; - } - - reg_value &= ~(0b0001 << 15); - reg_value |= 0x01 << 15; - - i2cWriteU16(_ads1115_addr, ADS1115_RA_CONFIG, reg_value); -} - -float Ammeter::getCurrent(bool calibration) { - if (calibration) { - return resolution * calibration_factor * getConversion() * AMMETER_MEASURING_DIR; - } else { - return resolution * getConversion() * AMMETER_MEASURING_DIR; - } -} - -int16_t Ammeter::getAdcRaw() { - uint16_t value = 0x00; - i2cReadU16(_ads1115_addr, ADS1115_RA_CONVERSION, &value); - adc_raw = value; - return value; -} - -int16_t Ammeter::getConversion(uint16_t timeout) { - if (_mode == SINGLESHOT) { - startSingleConversion(); - delay(cover_time); - uint64_t time = millis() + timeout; - while (time > millis() && isInConversion()); - } - - return getAdcRaw(); -} - -bool Ammeter::EEPORMWrite(uint8_t address, uint8_t* buff, uint8_t len) { - return i2cWriteBytes(_eeprom_addr, address, buff, len); -} - -bool Ammeter::EEPORMRead(uint8_t address, uint8_t* buff, uint8_t len) { - return i2cReadBytes(_eeprom_addr, address, buff, len); -} - -bool Ammeter::saveCalibration2EEPROM(ammeterGain_t gain, int16_t hope, int16_t actual) { - if (hope == 0 || actual == 0) { - return false; - } - - uint8_t buff[8]; - memset(buff, 0, 8); - buff[0] = gain; - buff[1] = hope >> 8; - buff[2] = hope & 0xFF; - - buff[3] = actual >> 8; - buff[4] = actual & 0xFF; - - for (uint8_t i = 0; i < 5; i++) { - buff[5] ^= buff[i]; - } - - uint8_t addr = getPGAEEEPROMAddr(gain); - return EEPORMWrite(addr, buff, 8); -} - -bool Ammeter::readCalibrationFromEEPROM(ammeterGain_t gain, int16_t* hope, int16_t* actual) { - uint8_t addr = getPGAEEEPROMAddr(gain); - uint8_t buff[8]; - memset(buff, 0, 8); - - *hope = 1; - *actual = 1; - - bool result = EEPORMRead(addr, buff, 8); - - if (result == false) { - return false; - } - - uint8_t xor_result = 0x00; - for (uint8_t i = 0; i < 5; i++) { - xor_result ^= buff[i]; - } - - if (xor_result != buff[5]) { - return false; - } - - *hope = (buff[1] << 8) | buff[2]; - *actual = (buff[3] << 8) | buff[4]; - return true; -} diff --git a/examples/Unit/AMeter_ADS1115/ammeter.h b/examples/Unit/AMeter_ADS1115/ammeter.h deleted file mode 100644 index 892244d..0000000 --- a/examples/Unit/AMeter_ADS1115/ammeter.h +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once - -#include "Arduino.h" - -#define ADS115_ADDR 0x48 -#define EEPROM_ADDR 0x51 - -#define ADS1115_RA_CONVERSION 0x00 -#define ADS1115_RA_CONFIG 0x01 - -#define ADS1115_PGA_6144 0x00 -#define ADS1115_PGA_4096 0x01 -#define ADS1115_PGA_2048 0x02 // default -#define ADS1115_PGA_1024 0x03 -#define ADS1115_PGA_512 0x04 -#define ADS1115_PGA_256 0x05 - -#define ADS1115_MV_6144 0.187500F -#define ADS1115_MV_4096 0.125000F -#define ADS1115_MV_2048 0.062500F // default -#define ADS1115_MV_1024 0.031250F -#define ADS1115_MV_512 0.015625F -#define ADS1115_MV_256 0.007813F - -#define ADS1115_RATE_8 0x00 -#define ADS1115_RATE_16 0x01 -#define ADS1115_RATE_32 0x02 -#define ADS1115_RATE_64 0x03 -#define ADS1115_RATE_128 0x04 // default -#define ADS1115_RATE_250 0x05 -#define ADS1115_RATE_475 0x06 -#define ADS1115_RATE_860 0x07 - -#define ADS1115_MUX_P0N1 0x00 // ammeter only support - -#define ADS1115_COMP_MODE_HYSTERESIS 0x00 // default -#define ADS1115_COMP_MODE_WINDOW 0x01 - -#define ADS1115_MODE_CONTINUOUS 0x00 -#define ADS1115_MODE_SINGLESHOT 0x01 // default - -#define AMMETER_PRESSURE_COEFFICIENT 0.05 - -#define AMMETER_PAG_6144_CAL_ADDR 208 -#define AMMETER_PAG_4096_CAL_ADDR 216 -#define AMMETER_PAG_2048_CAL_ADDR 224 -#define AMMETER_PAG_1024_CAL_ADDR 232 -#define AMMETER_PAG_512_CAL_ADDR 240 -#define AMMETER_PAG_256_CAL_ADDR 248 - -#define AMMETER_MEASURING_DIR -1 - -typedef enum { - PAG_6144 = ADS1115_PGA_6144, - PAG_4096 = ADS1115_PGA_4096, - PAG_2048 = ADS1115_PGA_2048, // default - PAG_1024 = ADS1115_PGA_1024, - PAG_512 = ADS1115_PGA_512, - PAG_256 = ADS1115_PGA_256, -} ammeterGain_t; - -typedef enum { - RATE_8 = ADS1115_RATE_8, - RATE_16 = ADS1115_RATE_16, - RATE_32 = ADS1115_RATE_32, - RATE_64 = ADS1115_RATE_64, - RATE_128 = ADS1115_RATE_128, // default - RATE_250 = ADS1115_RATE_250, - RATE_475 = ADS1115_RATE_475, - RATE_860 = ADS1115_RATE_860, -} ammeterRate_t; - -typedef enum { - SINGLESHOT = ADS1115_MODE_SINGLESHOT, - CONTINUOUS = ADS1115_MODE_CONTINUOUS, -} ammeterMode_t; - -class Ammeter { -private: - void i2cBegin(); - bool i2cReadBytes(uint8_t addr, uint8_t reg_addr, uint8_t* buff, uint16_t len); - bool i2cWriteBytes(uint8_t addr, uint8_t reg_addr, uint8_t* buff, uint16_t len); - bool i2cReadU16(uint8_t addr, uint8_t reg_addr, uint16_t* value); - bool i2cWriteU16(uint8_t addr, uint8_t reg_addr, uint16_t value); - - float getResolution(ammeterGain_t gain); - uint16_t getCoverTime(ammeterRate_t rate); - uint8_t getPGAEEEPROMAddr(ammeterGain_t gain); - - uint8_t _ads1115_addr; - uint8_t _eeprom_addr; - - float pga_256_calibration_5v_value = 10187; - float pga_2048_calibration_25v_value = 6368; - -public: - ammeterGain_t _gain; - ammeterRate_t _rate; - ammeterMode_t _mode; - - float resolution; - uint16_t cover_time; - int16_t adc_raw; - float calibration_factor; - - -public: - Ammeter(uint8_t ads1115_addr=ADS115_ADDR, uint8_t eeprom_addr=EEPROM_ADDR); - - void setGain(ammeterGain_t gain); - void setRate(ammeterRate_t rate); - void setMode(ammeterMode_t mode); - - float getCurrent(bool calibration = true); - int16_t getConversion(uint16_t timeout = 125); - int16_t getAdcRaw(); - - bool isInConversion(); - void startSingleConversion(); - - bool EEPORMWrite(uint8_t address, uint8_t* buff, uint8_t len); - bool EEPORMRead(uint8_t address, uint8_t* buff, uint8_t len); - - bool saveCalibration2EEPROM(ammeterGain_t gain, int16_t hope, int16_t actual); - bool readCalibrationFromEEPROM(ammeterGain_t gain, int16_t* hope, int16_t* actual); -}; diff --git a/examples/Unit/ANGLE/ANGLE.ino b/examples/Unit/ANGLE/ANGLE.ino index 40ec132..3d99d98 100644 --- a/examples/Unit/ANGLE/ANGLE.ino +++ b/examples/Unit/ANGLE/ANGLE.ino @@ -1,25 +1,38 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Angle. 角度计 +* date:2021/8/9 +******************************************************************************* + Description: Please connect to Port,Read the Angle of the angometer and convert it to digital display + 请连接端口,读取角度计的角度,并转换为数字量显示 +*/ + #include -// select the input pin for the potentiometer -int sensorPin = 33; -// last variable to store the value coming from the sensor -int last_sensorValue = 0; -// current variable to store the value coming from the sensor -int cur_sensorValue = 0; +int sensorPin = 33; // set the input pin for the potentiometer. 设置角度计的输入引脚 + +int last_sensorValue = 100; // Stores the value last read by the sensor. 存储传感器上次读取到的值 +int cur_sensorValue = 0; // Stores the value currently read by the sensor. 存储传感器当前读取到的值 void setup() { - M5.begin(); - M5.Lcd.setRotation(3); - pinMode(sensorPin, INPUT); - M5.Lcd.setCursor(0, 30); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.setTextSize(2); //Set the font size to 2. 设置字体大小为2 + pinMode(sensorPin, INPUT); //Sets the specified pin to input mode. 设置指定引脚为输入模式 + dacWrite(25, 0); M5.Lcd.print("the value of ANGLE: "); } void loop() { - // read the value from the sensor: - cur_sensorValue = analogRead(sensorPin); - M5.Lcd.setCursor(120, 30); - if(abs(cur_sensorValue - last_sensorValue) > 10){//debaunce - M5.Lcd.fillRect(120, 30, 100, 25, BLACK); + cur_sensorValue = analogRead(sensorPin); // read the value from the sensor. 读取当前传感器的值 + M5.Lcd.setCursor(0, 40); //Place the cursor at (0,40). 将光标固定在(0,40) + if(abs(cur_sensorValue - last_sensorValue) > 10){ //If the difference is more than 10. 如果差值超过10 + M5.Lcd.fillRect(0, 40, 100, 25, BLACK); M5.Lcd.print(cur_sensorValue); last_sensorValue = cur_sensorValue; } diff --git a/examples/Unit/BPS_BMP280/BPS_BMP280.ino b/examples/Unit/BPS_BMP280/BPS_BMP280.ino new file mode 100644 index 0000000..66ef382 --- /dev/null +++ b/examples/Unit/BPS_BMP280/BPS_BMP280.ino @@ -0,0 +1,41 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:BPS_BMP280. 压力传感器 +* date:2021/8/10 +******************************************** *********************************** + Please connect to Port,Read atmospheric pressure and temperature and display them on the display screen + 请连接端口,读取大气压强和温度并在显示屏上显示 +*/ +#include +#include //The BPS uses I2C comunication. +#include "Adafruit_Sensor.h" +#include + +Adafruit_BMP280 bme; + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + Wire.begin(); //Wire init, adding the I2C bus. Wire初始化, 加入i2c总线 + while (!bme.begin(0x76)){ //Init this sensor,True if the init was successful, otherwise false. 初始化传感器,如果初始化成功返回1 + M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!"); + } + M5.Lcd.fillScreen(BLACK); //Clear the screen. 清屏 +} + +float pressure,Temp; //Store the vuale of pressure and Temperature. 存储压力和温度() + +void loop() { + pressure = bme.readPressure(); + Temp = bme.readTemperature(); + M5.Lcd.setCursor(0, 0); //将光标设置在(0 ,0). Set the cursor to (0,0) + M5.Lcd.setTextSize(2); //设置字体大小为2. Set the font size to 2 + M5.Lcd.printf("Pre:%2.0fPa\nTem:%2.0f^C", pressure,Temp); + delay(100); +} diff --git a/examples/Unit/BPS_QMP6988/BPS_QMP6988.ino b/examples/Unit/BPS_QMP6988/BPS_QMP6988.ino new file mode 100644 index 0000000..408487f --- /dev/null +++ b/examples/Unit/BPS_QMP6988/BPS_QMP6988.ino @@ -0,0 +1,37 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:BPS_QMP6988. 压力传感器 +* date:2021/8/10 +****************************************************************************** + Please connect to Port A(33、32),Read atmospheric pressure and display them on the display screen + 请连接端口A(33、32),读取大气压强并在显示屏上显示 +*/ +#include +#include "Wire.h" //The BPS uses I2C comunication. +#include "UNIT_ENV.h" + +QMP6988 qmp6988; + +void setup() { + M5.begin(); //Init M5Stick. 初始化M5Stick + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + Wire.begin(32, 33); //Initialize pin 32,33. 初始化32,33引脚 +} + +void loop() { + while (!qmp6988.init()){ //Init qmp6988.True if the init was successful, otherwise false. 初始化qmp6988,如果初始化成功返回1 + M5.lcd.setTextSize(1); //Set the text size to 1. 设置文字大小为1 + M5.Lcd.println("\nCould not find a valid qmp6988 sensor, check wiring!"); + } + M5.lcd.setTextSize(2); //Set the text size to 2. 设置文字大小为2 + M5.lcd.setCursor(0,30); //Set the cursor at (0,30). 设置光标在(0,30)处 + M5.lcd.fillScreen(BLACK); //Fill the screen with black (to clear the screen). 将屏幕填充满黑色(用来清屏) + M5.Lcd.printf("Pre:%0.2f",qmp6988.calcPressure()); //Screen print format string. 屏幕打印格式化字符串 + delay(2000); //Delay 2s. 延迟2s +} \ No newline at end of file diff --git a/examples/Unit/BUTTON/BUTTON.ino b/examples/Unit/BUTTON/BUTTON.ino index 77f7d42..69df344 100644 --- a/examples/Unit/BUTTON/BUTTON.ino +++ b/examples/Unit/BUTTON/BUTTON.ino @@ -1,39 +1,46 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Button. 按键 +* date:2021/8/9 +******************************************************************************* + Please connect to Port,Read the button status of BUTTON Unit and display it on the screen + + 请连接端口,读取按键的状态并在显示屏上显示 +*/ #include -const int button = 33; int last_value = 0; int cur_value = 0; void setup() { - // init lcd - M5.begin(); - M5.Lcd.setRotation(3); - pinMode(button, INPUT); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(40, 0); M5.Lcd.println("Button example"); - Serial.println("Button example: "); + M5.begin(); //Init M5StickC. M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + pinMode(33, INPUT); //set pin mode to input.设置引脚模式为输入模式 + M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色 + M5.Lcd.println("Button example"); M5.Lcd.setTextColor(WHITE); } void loop() { - cur_value = digitalRead(button);// read the value of BUTTON - - M5.Lcd.setCursor(0,25); M5.Lcd.print("Status: "); - M5.Lcd.setCursor(0,45); M5.Lcd.print("Value: "); + cur_value = digitalRead(33); // read the value of BUTTON. 读取33号引脚的值 + M5.Lcd.setCursor(35,10); M5.Lcd.print("Button"); //Set the cursor position to (35,10). 将光标位置设置为(35,10) + M5.Lcd.setCursor(0,25); M5.Lcd.print("Value: "); + M5.Lcd.setCursor(0,45); M5.Lcd.print("State: "); if(cur_value != last_value){ - M5.Lcd.fillRect(95,25,100,25,BLACK); - M5.Lcd.fillRect(95,45,100,25,BLACK); + M5.Lcd.fillRect(20,25,75,85,BLACK); //Draw a black rectangle 75 by 85 at (20,25). 在(20,25)处绘制宽75,高85的黑色矩形 if(cur_value==0){ - M5.Lcd.setCursor(95,25); M5.Lcd.print("pressed");// display the status - M5.Lcd.setCursor(95,45); M5.Lcd.print("0"); - Serial.println("Button Status: pressed"); - Serial.println(" value: 0"); + M5.Lcd.setCursor(40,25); M5.Lcd.print("0"); // display the status. 在屏幕上显示按键的状态 + M5.Lcd.setCursor(40,45); M5.Lcd.print("pre"); } else{ - M5.Lcd.setCursor(95,25); M5.Lcd.print("released");// display the status - M5.Lcd.setCursor(95,45); M5.Lcd.print("1"); - Serial.println("Button Status: released"); - Serial.println(" value: 1"); + M5.Lcd.setCursor(40,25); M5.Lcd.print("1"); // display the status + M5.Lcd.setCursor(40,45); M5.Lcd.print("rel"); } last_value = cur_value; } diff --git a/examples/Unit/COLOR_TCS3472/COLOR_TCS3472.ino b/examples/Unit/COLOR_TCS3472/COLOR_TCS3472.ino new file mode 100644 index 0000000..b3f81bb --- /dev/null +++ b/examples/Unit/COLOR_TCS3472/COLOR_TCS3472.ino @@ -0,0 +1,70 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:COLOR_TCS3472. 颜色识别 +* date:2021/8/11 +******************************************************************************* + Please connect to Port ,Use COLOR Unit to read C, R, G, B values + 请连接端口,使用COLOR Unit读取C, R, G, B值 +*/ + +#include +#include "Adafruit_TCS34725.h" + +#define commonAnode true //set to false if using a common cathode LED. //如果使用普通阴极LED,则设置为false + +byte gammatable[256]; // our RGB -> eye-recognized gamma color + +static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) { + uint16_t _color; + _color = (uint16_t)(r & 0xF8) << 8; + _color |= (uint16_t)(g & 0xFC) << 3; + _color |= (uint16_t)(b & 0xF8) >> 3; + return _color; +} + +Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X); + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.println("Color View Test!"); + while(!tcs.begin()){ //如果color unit未能初始化 + M5.Lcd.println("No TCS34725 found ... check your connections"); + M5.Lcd.drawString("No Found sensor.",50, 100, 4); + delay(1000); + } + tcs.setIntegrationTime(TCS34725_INTEGRATIONTIME_154MS); //Sets the integration time for the TC34725. 设置TC34725的集成时间 + tcs.setGain(TCS34725_GAIN_4X); //Adjusts the gain on the TCS34725. 调整TCS34725上的增益 +} + +void loop() { + uint16_t clear, red, green, blue; + tcs.getRawData(&red, &green, &blue, &clear); //Reads the raw red, green, blue and clear channel values. 读取原始的红、绿、蓝和清晰的通道值 + + // Figure out some basic hex code for visualization. 生成对应的十六进制代码 + uint32_t sum = clear; + float r, g, b; + r = red; r /= sum; + g = green; g /= sum; + b = blue; b /= sum; + r *= 256; g *= 256; b *= 256; + uint16_t _color = color16((int)r, (int)g, (int)b); + + M5.lcd.setCursor(0,0); //Place the cursor at (0,0). 将光标固定在(0,0) + M5.lcd.fillRect(0,0,120,80,BLACK); //Fill the screen with a black rectangle. 将屏幕填充黑色矩形 + + M5.Lcd.print("C:"); M5.Lcd.println(clear); + M5.Lcd.print("R:"); M5.Lcd.println(red); + M5.Lcd.print("G:"); M5.Lcd.println(green); + M5.Lcd.print("B:"); M5.Lcd.println(blue); + M5.Lcd.print("0x"); + M5.Lcd.print((int)r, HEX); M5.Lcd.print((int)g, HEX); M5.Lcd.print((int)b, HEX); + + delay(1000); +} diff --git a/examples/Unit/CardKB/CardKB.ino b/examples/Unit/CardKB/CardKB.ino index 8720d25..482a771 100644 --- a/examples/Unit/CardKB/CardKB.ino +++ b/examples/Unit/CardKB/CardKB.ino @@ -1,32 +1,38 @@ -#include -#include +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:CardKB. 键盘 +* date:2021/8/11 +******************************************************************************* + Please connect to Port ,Read the characters entered by CardKB Unit and display them on the screen. + 请连接端口,读取CardKB Unit输入的字符并显示在屏幕上。 +*/ -#define CARDKB_ADDR 0x5F +#include +#define CARDKB_ADDR 0x5F //Define the I2C address of CardKB. 定义CardKB的I2C地址 -void setup() -{ - M5.begin(); - Wire.begin(32,33); - M5.Lcd.fillScreen(BLACK); - M5.Lcd.setRotation(3); - M5.Lcd.setCursor(1, 10); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setTextSize(1); +void setup(){ + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 M5.Lcd.printf("IIC Address: 0x5F\n"); M5.Lcd.printf(">>"); + Wire.begin(32, 33); //Initialize pin 32,33. 初始化32,33引脚 } + void loop() { - Wire.requestFrom(CARDKB_ADDR, 1); - while(Wire.available()) + Wire.requestFrom(CARDKB_ADDR, 1); //Request 1 byte from the slave device. 向从设备请求1字节 + while(Wire.available()) //If received data is detected. 如果检测到收到数据 { - char c = Wire.read(); // receive a byte as characterif + char c = Wire.read(); // Store the received data. 将接收到的数据存储 if (c != 0) { M5.Lcd.printf("%c", c); - Serial.println(c, HEX); - // M5.Speaker.beep(); } } - // delay(10); } diff --git a/examples/Unit/Color/Color.ino b/examples/Unit/Color/Color.ino deleted file mode 100644 index 690b61f..0000000 --- a/examples/Unit/Color/Color.ino +++ /dev/null @@ -1,74 +0,0 @@ -/* - Color test - hardware: M5Stack - - please install the Adfruit TCS34725 library first ... -*/ - - -#include -#include -#include "Adafruit_TCS34725.h" - - - -// set to false if using a common cathode LED -#define commonAnode true - -// our RGB -> eye-recognized gamma color -byte gammatable[256]; - -static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) { - uint16_t _color; - _color = (uint16_t)(r & 0xF8) << 8; - _color |= (uint16_t)(g & 0xFC) << 3; - _color |= (uint16_t)(b & 0xF8) >> 3; - return _color; -} - -Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X); - -void setup() { - delay(100); - - M5.begin(true, false, false); - Serial.begin(115200); - Serial.println("Color View Test!"); - - if (tcs.begin()) { - Serial.println("Found sensor"); - } else { - Serial.println("No TCS34725 found ... check your connections"); - while (1); // halt! - } - tcs.setIntegrationTime(TCS34725_INTEGRATIONTIME_154MS); - tcs.setGain(TCS34725_GAIN_4X); -} - -void loop() { - uint16_t clear, red, green, blue; - - - delay(60); // takes 50ms to read - - tcs.getRawData(&red, &green, &blue, &clear); - - - Serial.print("C:\t"); Serial.print(clear); - Serial.print("\tR:\t"); Serial.print(red); - Serial.print("\tG:\t"); Serial.print(green); - Serial.print("\tB:\t"); Serial.print(blue); - - // Figure out some basic hex code for visualization - uint32_t sum = clear; - float r, g, b; - r = red; r /= sum; - g = green; g /= sum; - b = blue; b /= sum; - r *= 256; g *= 256; b *= 256; - Serial.print("\t"); - Serial.print((int)r, HEX); Serial.print((int)g, HEX); Serial.print((int)b, HEX); - Serial.println(); - uint16_t _color = color16((int)r, (int)g, (int)b); - M5.Lcd.fillScreen(_color); -} diff --git a/examples/Unit/DAC_MCP4725/DAC_MCP4725.ino b/examples/Unit/DAC_MCP4725/DAC_MCP4725.ino index 1b97d01..f5a6ae2 100644 --- a/examples/Unit/DAC_MCP4725/DAC_MCP4725.ino +++ b/examples/Unit/DAC_MCP4725/DAC_MCP4725.ino @@ -1,35 +1,45 @@ /* - hardware : m5stack uint dac - - please install adafruit MCP4725 lib +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:DAC_MCP4725. 数模转换 +* date:2021/8/16 +******************************************************************************* + Please connect to Port,Use DAC Unit to output 0 ~ 3.3V voltage with an accuracy of 12 bits. + 请连接端口,使用DAC Unit 输出12位精度的0 ~ 3.3V电压。 */ + #include -#include #include -#define DAC_ADDR +#define DAC_ADDR 0x60 // For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC) + // For MCP4725A0 the address is 0x60 or 0x61 + // For MCP4725A2 the address is 0x64 or 0x65 + Adafruit_MCP4725 dac; void setup(void) { - - M5.begin(); - Serial.begin(115200); - Serial.println("Hello!"); - - // For Adafruit MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC) - // For MCP4725A0 the address is 0x60 or 0x61 - // For MCP4725A2 the address is 0x64 or 0x65 - dac.begin(0x60); - - Serial.println("Generating a triangle wave"); - dac.setVoltage(2048, false); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.lcd.setTextSize(2); //Set the text size to 2. 设置文字大小为2 + M5.Lcd.print("DAC MCP4725 demo."); + dac.begin(0x60); //Setups the hardware address and checks the DAC was found. 设置硬件地址并检查是否找到DAC + dac.setVoltage(2048, false); } void loop(void) { - // 12bit value , false mean not write EEPROM - dac.setVoltage(1024, false); - delay(1000); - dac.setVoltage(2048, false); - delay(1000); -} + M5.Lcd.setCursor(40,60); + M5.Lcd.print("1.2V"); + dac.setVoltage(1024, false); //Set the voltage to 1.2V and retain the current voltage output after power off or reset. 设置电压为1.2v,关闭断电或复位后保留当前电压输出 + delay(1000); + M5.Lcd.fillRect(40,60,120,40,BLACK); //Draw a black rectangle at (100,60) with a width of 120 and a height of 40. 在(100,60)处绘制一个宽为120,高为40的黑色矩形 + M5.Lcd.print("2.4V"); + dac.setVoltage(2048, false); //2.4v + delay(1000); + M5.Lcd.fillRect(40,60,120,40,BLACK); +} \ No newline at end of file diff --git a/examples/Unit/Dual_Button/Dual_Button.ino b/examples/Unit/Dual_Button/Dual_Button.ino index 7fd8903..9af8e15 100644 --- a/examples/Unit/Dual_Button/Dual_Button.ino +++ b/examples/Unit/Dual_Button/Dual_Button.ino @@ -1,69 +1,61 @@ /* - This Unit connects to GRVOE B on M5Core. +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Core sample source code +* 配套 M5Core 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/gray +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/gray +* +* describe:Button_Two. 双按键 +* date:2021/8/9 +******************************************************************************* + Please connect to Port,Read the button status of BUTTON Unit and display it on the screen + + 请连接端口,读取按键的状态并在显示屏上显示 */ #include -int last_value_red = 0; -int cur_value_red = 0; -int last_value_blue = 0; -int cur_value_blue = 0; +int last_value1 = 0,last_value2 = 0; +int cur_value1 = 0,cur_value2 = 0; void setup() { - // init lcd - M5.begin(); - M5.Lcd.setRotation(3); + M5.begin(); //Init M5Stack. 初始化M5Stack + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + pinMode(33, INPUT); //set pin mode to input.设置引脚模式为输入模式 pinMode(32, INPUT); - pinMode(33, INPUT); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(15, 2); - M5.Lcd.println("Dual Button example"); + M5.Lcd.setTextColor(YELLOW); //Set the font color to yellow. 设置字体颜色为黄色 + M5.Lcd.println("Button example"); M5.Lcd.setTextColor(WHITE); } void loop() { - cur_value_red = digitalRead(32); - cur_value_blue = digitalRead(33); - - M5.Lcd.setCursor(0,15); M5.Lcd.print("Blue Status: "); - M5.Lcd.setCursor(0,30); M5.Lcd.print("Blue Value: "); - M5.Lcd.setCursor(0,45); M5.Lcd.print(" Red Status: "); - M5.Lcd.setCursor(0,60); M5.Lcd.print(" Red Value: "); - - if(cur_value_blue != last_value_blue){ - M5.Lcd.fillRect(95,15,100,15,BLACK); - M5.Lcd.fillRect(95,30,100,15,BLACK); - if(cur_value_blue==0){ - M5.Lcd.setCursor(95,15); M5.Lcd.print("pressed"); - M5.Lcd.setCursor(95,30); M5.Lcd.print("0"); - Serial.println("Button Status: blue pressed"); - Serial.println(" value: 0"); + cur_value1 = digitalRead(33); // read the value of BUTTON. 读取33号引脚的值 + cur_value2 = digitalRead(32); + M5.Lcd.setCursor(35,10); M5.Lcd.print("Btn.1 Btn.2"); //Set the cursor position to (35,10). 将光标位置设置为(35,10) + M5.Lcd.setCursor(0,25); M5.Lcd.print("Value: "); + M5.Lcd.setCursor(0,45); M5.Lcd.print("State: "); + if(cur_value1 != last_value1){ + M5.Lcd.fillRect(20,25,45,40,BLACK); //Draw a black rectangle 45 by 40 at (20,25). 在(20,25)处绘制宽45,高40的黑色矩形 + if(cur_value1==0){ + M5.Lcd.setCursor(40,25); M5.Lcd.print("0"); // display the status + M5.Lcd.setCursor(40,45); M5.Lcd.print("pre"); } else{ - M5.Lcd.setCursor(95,15); M5.Lcd.print("released"); - M5.Lcd.setCursor(95,30); M5.Lcd.print("1"); - Serial.println("Button Status: blue released"); - Serial.println(" value: 1"); + M5.Lcd.setCursor(40,25); M5.Lcd.print("1"); // display the status + M5.Lcd.setCursor(40,45); M5.Lcd.print("rel"); } - last_value_blue = cur_value_blue; + last_value1 = cur_value1; } - - if(cur_value_red != last_value_red){ - M5.Lcd.fillRect(95,45,100,15,BLACK); - M5.Lcd.fillRect(95,60,100,15,BLACK); - if(cur_value_red==0){ - M5.Lcd.setCursor(95,45); M5.Lcd.print("pressed"); - M5.Lcd.setCursor(95,60); M5.Lcd.print("0"); - Serial.println("Button Status: red pressed"); - Serial.println(" value: 0"); - } - else{ - M5.Lcd.setCursor(95,45); M5.Lcd.print("released"); - M5.Lcd.setCursor(95,60); M5.Lcd.print("1"); - Serial.println("Button Status: red released"); - Serial.println(" value: 1"); - } - last_value_red = cur_value_red; + if(cur_value2 != last_value2){ + M5.Lcd.fillRect(70,25,45,40,BLACK); + if(cur_value2==0){ + M5.Lcd.setCursor(80,25); M5.Lcd.print("0"); // display the status + M5.Lcd.setCursor(80,45); M5.Lcd.print("pre"); + } + else{ + M5.Lcd.setCursor(80,25); M5.Lcd.print("1"); // display the status + M5.Lcd.setCursor(80,45); M5.Lcd.print("rel"); + } + last_value2 = cur_value2; } - - M5.update(); } diff --git a/examples/Unit/ENVIII_SHT30_QMP6988/ENVIII_SHT30_QMP6988.ino b/examples/Unit/ENVIII_SHT30_QMP6988/ENVIII_SHT30_QMP6988.ino new file mode 100644 index 0000000..5966803 --- /dev/null +++ b/examples/Unit/ENVIII_SHT30_QMP6988/ENVIII_SHT30_QMP6988.ino @@ -0,0 +1,47 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:ENVIII_SHT30_QMP6988. 环境传感器 +* date:2021/8/17 +******************************************************************************* + Please connect to Port,Read temperature, humidity and atmospheric pressure and display them on the display screen + 请连接端口,读取温度、湿度和大气压强并在显示屏上显示 +*/ +#include +#include "Adafruit_Sensor.h" +#include +#include "UNIT_ENV.h" + +SHT3X sht30; +QMP6988 qmp6988; + +float tmp = 0.0; +float hum = 0.0; +float pressure = 0.0; + +void setup() { + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + Wire.begin(); //Wire init, adding the I2C bus. Wire初始化, 加入i2c总线 + qmp6988.init(); + M5.lcd.println(F("ENV Unit III test")); +} + +void loop() { + pressure = qmp6988.calcPressure(); + if(sht30.get()==0){ //Obtain the data of shT30. 获取sht30的数据 + tmp = sht30.cTemp; //Store the temperature obtained from shT30. 将sht30获取到的温度存储 + hum = sht30.humidity; //Store the humidity obtained from the SHT30. 将sht30获取到的湿度存储 + }else{ + tmp=0,hum=0; + } + M5.lcd.fillRect(0,20,100,60,BLACK); //Fill the screen with black (to clear the screen). 将屏幕填充黑色(用来清屏) + M5.lcd.setCursor(0,20); + M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\nPressure:%2.0fPa\r\n", tmp, hum, pressure); + delay(2000); +} diff --git a/examples/Unit/ENVII_SHT30_BMP280/Adafruit_Sensor.h b/examples/Unit/ENVII_SHT30_BMP280/Adafruit_Sensor.h deleted file mode 100644 index 8ac638f..0000000 --- a/examples/Unit/ENVII_SHT30_BMP280/Adafruit_Sensor.h +++ /dev/null @@ -1,156 +0,0 @@ -/* -* Copyright (C) 2008 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software< /span> -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and - * extended sensor support to include color, voltage and current */ - -#ifndef _ADAFRUIT_SENSOR_H -#define _ADAFRUIT_SENSOR_H - -#ifndef ARDUINO - #include -#elif ARDUINO >= 100 - #include "Arduino.h" - #include "Print.h" -#else - #include "WProgram.h" -#endif - -/* Intentionally modeled after sensors.h in the Android API: - * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h */ - -/* Constants */ -#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ -#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ -#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ -#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) -#define SENSORS_MAGFIELD_EARTH_MAX (60.0F) /**< Maximum magnetic field on Earth's surface */ -#define SENSORS_MAGFIELD_EARTH_MIN (30.0F) /**< Minimum magnetic field on Earth's surface */ -#define SENSORS_PRESSURE_SEALEVELHPA (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ -#define SENSORS_DPS_TO_RADS (0.017453293F) /**< Degrees/s to rad/s multiplier */ -#define SENSORS_GAUSS_TO_MICROTESLA (100) /**< Gauss to micro-Tesla multiplier */ - -/** Sensor types */ -typedef enum -{ - SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ - SENSOR_TYPE_MAGNETIC_FIELD = (2), - SENSOR_TYPE_ORIENTATION = (3), - SENSOR_TYPE_GYROSCOPE = (4), - SENSOR_TYPE_LIGHT = (5), - SENSOR_TYPE_PRESSURE = (6), - SENSOR_TYPE_PROXIMITY = (8), - SENSOR_TYPE_GRAVITY = (9), - SENSOR_TYPE_LINEAR_ACCELERATION = (10), /**< Acceleration not including gravity */ - SENSOR_TYPE_ROTATION_VECTOR = (11), - SENSOR_TYPE_RELATIVE_HUMIDITY = (12), - SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), - SENSOR_TYPE_VOLTAGE = (15), - SENSOR_TYPE_CURRENT = (16), - SENSOR_TYPE_COLOR = (17) -} sensors_type_t; - -/** struct sensors_vec_s is used to return a vector in a common format. */ -typedef struct { - union { - float v[3]; - struct { - float x; - float y; - float z; - }; - /* Orientation sensors */ - struct { - float roll; /**< Rotation around the longitudinal axis (the plane body, 'X axis'). Roll is positive and increasing when moving downward. -90°<=roll<=90° */ - float pitch; /**< Rotation around the lateral axis (the wing span, 'Y axis'). Pitch is positive and increasing when moving upwards. -180°<=pitch<=180°) */ - float heading; /**< Angle between the longitudinal axis (the plane body) and magnetic north, measured clockwise when viewing from the top of the device. 0-359° */ - }; - }; - int8_t status; - uint8_t reserved[3]; -} sensors_vec_t; - -/** struct sensors_color_s is used to return color data in a common format. */ -typedef struct { - union { - float c[3]; - /* RGB color space */ - struct { - float r; /**< Red component */ - float g; /**< Green component */ - float b; /**< Blue component */ - }; - }; - uint32_t rgba; /**< 24-bit RGBA value */ -} sensors_color_t; - -/* Sensor event (36 bytes) */ -/** struct sensor_event_s is used to provide a single sensor event in a common format. */ -typedef struct -{ - int32_t version; /**< must be sizeof(struct sensors_event_t) */ - int32_t sensor_id; /**< unique sensor identifier */ - int32_t type; /**< sensor type */ - int32_t reserved0; /**< reserved */ - int32_t timestamp; /**< time is in milliseconds */ - union - { - float data[4]; - sensors_vec_t acceleration; /**< acceleration values are in meter per second per second (m/s^2) */ - sensors_vec_t magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ - sensors_vec_t orientation; /**< orientation values are in degrees */ - sensors_vec_t gyro; /**< gyroscope values are in rad/s */ - float temperature; /**< temperature is in degrees centigrade (Celsius) */ - float distance; /**< distance in centimeters */ - float light; /**< light in SI lux units */ - float pressure; /**< pressure in hectopascal (hPa) */ - float relative_humidity; /**< relative humidity in percent */ - float current; /**< current in milliamps (mA) */ - float voltage; /**< voltage in volts (V) */ - sensors_color_t color; /**< color in RGB component values */ - }; -} sensors_event_t; - -/* Sensor details (40 bytes) */ -/** struct sensor_s is used to describe basic information about a specific sensor. */ -typedef struct -{ - char name[12]; /**< sensor name */ - int32_t version; /**< version of the hardware + driver */ - int32_t sensor_id; /**< unique sensor identifier */ - int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */ - float max_value; /**< maximum value of this sensor's value in SI units */ - float min_value; /**< minimum value of this sensor's value in SI units */ - float resolution; /**< smallest difference between two values reported by this sensor */ - int32_t min_delay; /**< min delay in microseconds between events. zero = not a constant rate */ -} sensor_t; - -class Adafruit_Sensor { - public: - // Constructor(s) - Adafruit_Sensor() {} - virtual ~Adafruit_Sensor() {} - - // These must be defined by the subclass - virtual void enableAutoRange(bool enabled) { (void)enabled; /* suppress unused warning */ }; - virtual bool getEvent(sensors_event_t*) = 0; - virtual void getSensor(sensor_t*) = 0; - - private: - bool _autoRange; -}; - -#endif diff --git a/examples/Unit/ENVII_SHT30_BMP280/ENVII_SHT30_BMP280.ino b/examples/Unit/ENVII_SHT30_BMP280/ENVII_SHT30_BMP280.ino index d85fd2a..ecd0b69 100644 --- a/examples/Unit/ENVII_SHT30_BMP280/ENVII_SHT30_BMP280.ino +++ b/examples/Unit/ENVII_SHT30_BMP280/ENVII_SHT30_BMP280.ino @@ -1,13 +1,22 @@ /* - Description: Use ENV II Unit to read temperature, humidity, atmospheric pressure, and display the data on the screen. - Please install library before compiling: - Adafruit BMP280: https://github.com/adafruit/Adafruit_BMP280_Library +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:ENVII_SHT30_BMP280. 环境传感器 +* date:2021/8/11 +******************************************************************************* + Please connect to Port,Read temperature, humidity and atmospheric pressure and display them on the display screen + 请连接端口,读取温度、湿度和大气压强并在显示屏上显示 */ + #include -#include #include "Adafruit_Sensor.h" #include -#include "SHT3X.h" +#include "UNIT_ENV.h" SHT3X sht30; Adafruit_BMP280 bme; @@ -16,40 +25,22 @@ float hum = 0.0; float pressure = 0.0; void setup() { - M5.begin(); - Wire.begin(32,33); - M5.Lcd.setTextSize(1); - Serial.println(F("ENV Unit(SHT30 and BMP280) test...")); - - while (!bme.begin(0x76)){ - Serial.println("Could not find a valid BMP280 sensor, check wiring!"); - M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!"); - } - M5.Lcd.fillRect(0, 0, 80, 160, BLACK); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.lcd.println(F("ENV Unit(SHT30 and BMP280) test...\n")); + Wire.begin(32, 33); //Initialize pin 32,33. 初始化32,33引脚 } void loop() { - pressure = bme.readPressure(); - if(sht30.get()==0){ - tmp = sht30.cTemp; - hum = sht30.humidity; + while (!bme.begin(0x76)){ //初始化bme传感器. Init the sensor of bme + M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!"); } - Serial.printf("Temperatura: %2.2f*C Humedad: %0.2f%% Pressure: %0.2fPa\r\n", tmp, hum, pressure); - - M5.Lcd.setTextColor(WHITE, BLACK); - M5.Lcd.setCursor(2, 0); - M5.Lcd.printf("Temp:"); - M5.Lcd.setCursor(5, 10); - M5.Lcd.printf("%2.1f", tmp); - M5.Lcd.setCursor(2, 25); - M5.Lcd.printf("Humi:"); - M5.Lcd.setCursor(5, 35); - M5.Lcd.printf("%2.0f%%",hum); - M5.Lcd.setCursor(2, 50); - M5.Lcd.printf("Pressure:"); - M5.Lcd.setCursor(5, 60); - M5.Lcd.printf("%2.0fPa", pressure); - - delay(100); - + pressure = bme.readPressure(); //Stores the pressure gained by BMP. 存储bmp获取到的压强 + sht30.get(); //Obtain the data of shT30. 获取sht30的数据 + tmp = sht30.cTemp; //Store the temperature obtained from shT30. 将sht30获取到的温度存储 + hum = sht30.humidity; //Store the humidity obtained from the SHT30. 将sht30获取到的湿度存储 + M5.lcd.setCursor(0,20); + M5.lcd.fillRect(0,20,100,60,BLACK); //Fill the screen with black (to clear the screen). 将屏幕填充满黑色(用来清屏) + M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\nPressure:%2.0fPa\r\n", tmp, hum, pressure); + delay(2000); } diff --git a/examples/Unit/ENVII_SHT30_BMP280/SHT3X.cpp b/examples/Unit/ENVII_SHT30_BMP280/SHT3X.cpp deleted file mode 100644 index 5ca8ebb..0000000 --- a/examples/Unit/ENVII_SHT30_BMP280/SHT3X.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "SHT3X.h" - -/* Motor() - -*/ -SHT3X::SHT3X(uint8_t address) -{ - Wire.begin(); - _address=address; -} - - - -byte SHT3X::get() -{ - unsigned int data[6]; - - // Start I2C Transmission - Wire.beginTransmission(_address); - // Send measurement command - Wire.write(0x2C); - Wire.write(0x06); - // Stop I2C transmission - if (Wire.endTransmission()!=0) - return 1; - - delay(500); - - // Request 6 bytes of data - Wire.requestFrom(_address, 6); - - // Read 6 bytes of data - // cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc - for (int i=0;i<6;i++) { - data[i]=Wire.read(); - }; - - delay(50); - - if (Wire.available()!=0) - return 2; - - // Convert the data - cTemp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45; - fTemp = (cTemp * 1.8) + 32; - humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0); - - return 0; -} diff --git a/examples/Unit/ENVII_SHT30_BMP280/SHT3X.h b/examples/Unit/ENVII_SHT30_BMP280/SHT3X.h deleted file mode 100644 index b6a2348..0000000 --- a/examples/Unit/ENVII_SHT30_BMP280/SHT3X.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __SHT3X_H -#define __HT3X_H - - -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -#include "Wire.h" - -class SHT3X{ -public: - SHT3X(uint8_t address=0x44); - byte get(void); - float cTemp=0; - float fTemp=0; - float humidity=0; - -private: - uint8_t _address; - -}; - - -#endif diff --git a/examples/Unit/ENV_DHT12_BMP280/ENV_DHT12_BMP280.ino b/examples/Unit/ENV_DHT12_BMP280/ENV_DHT12_BMP280.ino new file mode 100644 index 0000000..bed4e08 --- /dev/null +++ b/examples/Unit/ENV_DHT12_BMP280/ENV_DHT12_BMP280.ino @@ -0,0 +1,43 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:ENVII_DH12_BMP280. 环境传感器 +* date:2021/8/17 +******************************************************************************* + Please connect to Port,Read temperature, humidity and atmospheric pressure and display them on the display screen + 请连接端口,读取温度、湿度和大气压强并在显示屏上显示 +*/ + +#include +#include "Adafruit_Sensor.h" +#include +#include "UNIT_ENV.h" + +DHT12 dht12; +Adafruit_BMP280 bme; + +void setup() { + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + Wire.begin(); //Wire init, adding the I2C bus. Wire初始化, 加入i2c总线 + M5.Lcd.println(F("ENV Unit test")); +} + +void loop() { + while (!bme.begin(0x76)){ + M5.Lcd.println("Could not find a valid BMP280 sensor, check wiring!"); + } + float tmp = dht12.readTemperature(); //Store the temperature obtained from dht12. 将dht12获取到的温度存储 + float hum = dht12.readHumidity(); //Store the humidity obtained from the dht12. 将dht12获取到的湿度存储 + float pressure = bme.readPressure(); //Stores the pressure gained by BMP. 存储bmp获取到的压强 + M5.lcd.fillRect(0,10,160,80,BLACK); //Fill the screen with black (to clear the screen). 将屏幕填充黑色(用来清屏) + M5.lcd.setCursor(0,10); + M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\nPressure:%2.0fPa\r\n", tmp, hum, pressure); + + delay(2000); +} diff --git a/examples/Unit/Earth/Earth.ino b/examples/Unit/Earth/Earth.ino index b8f446a..b44b6df 100644 --- a/examples/Unit/Earth/Earth.ino +++ b/examples/Unit/Earth/Earth.ino @@ -1,33 +1,30 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:EARTH. 土壤湿度 +* date:2021/8/11 +******************************************************************************* + Please connect to Port,Read the analog quantity and digital quantity returned by the EARTH unit, and convert the analog quantity into 12-bit data and display it on the screen. + 请连接端口,读取EARTH Unit 返回的模拟量和数字量,并将模拟量转换为12位数据显示在屏幕上。 +*/ + #include void setup() { - M5.begin(); - M5.Lcd.setRotation(3); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(25, 2); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 M5.Lcd.printf("UNIT_EARTH EXAMPLE\n"); - pinMode(32, INPUT); - M5.Lcd.setCursor(10, 30); - M5.Lcd.print("GPIO33:"); - M5.Lcd.setCursor(10, 50); - M5.Lcd.print("GPIO32:"); + pinMode(22, INPUT); //Set pin 22 to input mode. 将引脚22设置为输入模式 } -uint16_t analogRead_value = 0; -uint16_t digitalRead_value = 0; void loop() { - // put your main code here, to run repeatedly: - M5.Lcd.setCursor(80, 30); - M5.Lcd.setTextColor(BLACK); - M5.Lcd.printf("%d\n", analogRead_value); - M5.Lcd.setCursor(80, 50); - M5.Lcd.printf("%d\n", digitalRead_value); - analogRead_value = analogRead(33); - digitalRead_value = digitalRead(32); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(80, 30); - M5.Lcd.printf("%d\n", analogRead_value); - M5.Lcd.setCursor(80, 50); - M5.Lcd.printf("%d\n", digitalRead_value); - delay(10); + M5.Lcd.setCursor(0, 30); //Set the cursor at (0,30). 将光标设置在(0,30) + M5.Lcd.printf("AnalogRead:%d\n", analogRead(33)); + M5.Lcd.printf("DigitalRead:%d\n", digitalRead(22)); + delay(1000); } diff --git a/examples/Unit/FADER/FADER.ino b/examples/Unit/FADER/FADER.ino new file mode 100644 index 0000000..b35d3d3 --- /dev/null +++ b/examples/Unit/FADER/FADER.ino @@ -0,0 +1,55 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/cn/unit/fader +* 获取更多资料请访问:https://docs.m5stack.com/en/unit/fader +* +* describe:UNIT FADER. 滑动电位器/推子 +* date:2021/8/20 +******************************************************************************* + Connect UNIT FADER to port and push the FADER slider to adjust the input value and light brightness + 将UNIT FADER连接到端口, 推动FADER滑杆即可实现调整输入数值大小与灯光亮度 +*/ + +#include "M5StickC.h" +#include "FastLED.h" + +// How many leds in your strip? +#define NUM_LEDS 14 +#define INPUT_PINS 36 + +#define DATA_PIN 26 + +// Define the array of leds +CRGB leds[NUM_LEDS]; + +uint8_t beginHue = 0; +uint8_t deltaHue = 30; +uint8_t brightness = 100; +uint16_t rawADC = 0; + +void setup() +{ + M5.begin(); + M5.Lcd.setTextDatum(MC_DATUM); + M5.Lcd.drawString("FADER UNIT TEST", 160, 60, 4); + FastLED.addLeds(leds, NUM_LEDS); + delay(1000); + pinMode(36, INPUT); + fill_rainbow(leds, NUM_LEDS, beginHue, deltaHue); +} + + +void loop() +{ + rawADC = analogRead(INPUT_PINS); //Read ADC value 读取ADC数值 + brightness = map(rawADC, 0, 4095, 0, 255); //The mapping ADC value is the brightness value range 映射ADC值为亮度值范围 + FastLED.setBrightness(brightness); //Adjust the brightness of the FADER LED 调整FADER LED灯亮度 + FastLED.show(); + Serial.printf("%d\r\n", rawADC); + M5.Lcd.fillRect(0, 120, 320, 100, BLACK); + M5.Lcd.drawString("value: "+String(rawADC), 160, 160, 4); + delay(100); +} \ No newline at end of file diff --git a/examples/Unit/FAN/FAN.ino b/examples/Unit/FAN/FAN.ino index ebfa65e..1492cff 100644 --- a/examples/Unit/FAN/FAN.ino +++ b/examples/Unit/FAN/FAN.ino @@ -1,27 +1,36 @@ /* - Description: Adjust the speed of FAN Unit through PWM. +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Fan. 风扇 +* date:2021/8/11 +******************************************************************************* + Please connect to Port, Adjust the speed of FAN Unit through PWM. + 请连接端口,通过PWM调节风扇单元的转速。 */ - #include -const int motor_pin = 32; +#define motor_pin 32 + int freq = 10000; int ledChannel = 0; int resolution = 10; void setup() { - // put your setup code here, to run once: - M5.begin(); - M5.Lcd.setCursor(20, 10, 2); - M5.Lcd.println("MOTOR"); - ledcSetup(ledChannel, freq, resolution); - ledcAttachPin(motor_pin, ledChannel); - + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.setCursor(65, 10); //Set the cursor at (65,10). 将光标设置在(65,10)处 + M5.Lcd.println("Fan"); + ledcSetup(ledChannel, freq, resolution); //Sets the frequency and number of counts corresponding to the channel. 设置通道对应的频率和计数位数 + ledcAttachPin(motor_pin, ledChannel); //Binds the specified channel to the specified I/O port for output. 将指定通道绑定到指定 IO 口上以实现输出 } void loop() { - // put your main code here, to run repeatedly: - ledcWrite(ledChannel, 512); - delay(1000); - ledcWrite(ledChannel, 0); - delay(1000); + ledcWrite(ledChannel, 512); //Output PWM. 输出PWM + delay(1000); + ledcWrite(ledChannel, 0); + delay(1000); } diff --git a/examples/Unit/HALL/HALL.ino b/examples/Unit/HALL/HALL.ino index 9c63ebc..4d92e98 100644 --- a/examples/Unit/HALL/HALL.ino +++ b/examples/Unit/HALL/HALL.ino @@ -1,30 +1,35 @@ -//Low-level signal can be generated when the magnet S pole is close to the front of the sensor -//OR the N pole is close to the back, and the internal LED indicator will light up, the screen wiil display 0. +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Hall. 霍尔传感器 +* date:2021/8/18 +******************************************************************************* + Please connect to Port ,Displays a string on the screen. + 请连接端口,在屏幕上显示字符串。 + Low-level signal can be generated when the magnet S pole is close to the front of the sensor + 当磁体S极靠近传感器前端时,会产生低电平信号 + OR the N pole is close to the back, and the internal LED indicator will light up, the screen wiil display 0. + 或N极靠近背面,内部LED指示灯亮起,屏幕显示0。 +*/ #include -#define HALL 33 -void header() -{ - M5.Lcd.setTextSize(1); - M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); - M5.Lcd.drawString("HALL", 25, 5, 2); - M5.Lcd.drawString("Sensor", 20, 20, 2); -} +#define HALL 33 void setup() { - M5.begin(true, false, true); - header(); - pinMode(HALL, INPUT); + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + M5.Lcd.print(" HALL Sensor"); + pinMode(HALL, INPUT); //Set the pins to which the Hall sensor is connected to the input mode. 将霍尔传感器所连接的引脚设置为输入模式 } void loop() { - bool status = digitalRead(HALL); - M5.Lcd.setTextSize(1); - M5.Lcd.setCursor(30, 48, 2); - M5.Lcd.printf("Hall"); - M5.Lcd.setCursor(20, 63, 2); - M5.Lcd.printf("status:"); - M5.Lcd.setCursor(15, 80, 8); - M5.Lcd.printf("%d", status); + bool status = digitalRead(HALL); + M5.Lcd.setCursor(20, 30); + M5.Lcd.printf("Hall status : %d", status); } diff --git a/examples/Unit/HEART/MAX30100_RawData/MAX30100_RawData.ino b/examples/Unit/HEART/MAX30100_RawData/MAX30100_RawData.ino index d331c43..5a70b55 100644 --- a/examples/Unit/HEART/MAX30100_RawData/MAX30100_RawData.ino +++ b/examples/Unit/HEART/MAX30100_RawData/MAX30100_RawData.ino @@ -1,71 +1,58 @@ /* - pls install MAX30100lib by library manager first - addr: https://github.com/oxullo/Arduino-MAX30100 +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Heart Rate. 心率 +* date:2021/8/16 +******************************************************************************* + Please connect to Port , HEART Unit obtains the original value of the heart rate detection and displays it on the screen. The user can also use the Arduino IDE Serial Plotter to view the line graph output. + 请连接端口,HEART Unit获取心率检测的原始值并显示在屏幕上。用户还可以使用Arduino IDE Serial Plotter查看线图输出。 */ #include -#include #include "MAX30100.h" -// Sampling is tightly related to the dynamic range of the ADC. -// refer to the datasheet for further info #define SAMPLING_RATE MAX30100_SAMPRATE_100HZ - -// The LEDs currents must be set to a level that avoids clipping and maximises the -// dynamic range -#define IR_LED_CURRENT MAX30100_LED_CURR_50MA +#define IR_LED_CURRENT MAX30100_LED_CURR_24MA #define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA - -// The pulse width of the LEDs driving determines the resolution of -// the ADC (which is a Sigma-Delta). -// set HIGHRES_MODE to true only when setting PULSE_WIDTH to MAX30100_SPC_PW_1600US_16BITS #define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS #define HIGHRES_MODE true - -// Instantiate a MAX30100 sensor class -MAX30100 sensor; +MAX30100 sensor; // Instantiate a MAX30100 sensor class. 实例化一个MAX30100传感器类 void setup() -{ - M5.begin(); - M5.Lcd.setRotation(3); - //M5.Lcd.setTextColor(WHITE); - M5.Lcd.setTextSize(3); - M5.Lcd.print("MAX30100"); - //M5.Lcd.clear(BLACK); - M5.Lcd.setCursor(0, 40); - Serial.begin(115200); - - Serial.print("Initializing MAX30100.."); - - // Initialize the sensor - // Failures are generally due to an improper I2C wiring, missing power supply - // or wrong target chip - if (!sensor.begin()) { - Serial.println("FAILED"); - for(;;); - } else { - Serial.println("SUCCESS"); - } - - // Set up the wanted parameters - sensor.setMode(MAX30100_MODE_SPO2_HR); - sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT); - sensor.setLedsPulseWidth(PULSE_WIDTH); - sensor.setSamplingRate(SAMPLING_RATE); - sensor.setHighresModeEnabled(HIGHRES_MODE); +{ + M5.begin(); //Init M5StickC. 初始化M5StickC + Serial.print("Initializing MAX30100.."); + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + + while (!sensor.begin()) { // Initialize the sensor. 初始化传感器 + M5.Lcd.setCursor(0, 0); + M5.Lcd.println("Sensor not found"); + delay(1000); + } + M5.Lcd.fillScreen(BLACK); + // Set up the wanted parameters. 设置所需的参数 + sensor.setMode(MAX30100_MODE_SPO2_HR); + sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT); + sensor.setLedsPulseWidth(PULSE_WIDTH); + sensor.setSamplingRate(SAMPLING_RATE); + sensor.setHighresModeEnabled(HIGHRES_MODE); } void loop() { - uint16_t ir, red; - - sensor.update(); - - while (sensor.getRawValues(&ir, &red)) { - Serial.print(ir); - Serial.print('\t'); - Serial.println(red); - } + uint16_t ir, red; + sensor.update(); //更新传感器读取到的数据 + while (sensor.getRawValues(&ir, &red)) { //如果获取到数据 + M5.Lcd.setTextFont(4); + M5.Lcd.setCursor(0, 20); + M5.Lcd.printf("IR:%d ",ir); + M5.Lcd.setCursor(0, 40); + M5.Lcd.printf("RED:%d ",red); + } } diff --git a/examples/Unit/HEX_SK6812/HEX_SK6812.ino b/examples/Unit/HEX_SK6812/HEX_SK6812.ino index 927a96e..3ff9543 100644 --- a/examples/Unit/HEX_SK6812/HEX_SK6812.ino +++ b/examples/Unit/HEX_SK6812/HEX_SK6812.ino @@ -1,8 +1,18 @@ /* - Description: Control HEX Unit to run rainbow light show - Please install library before compiling: - FastLED: https://github.com/FastLED/FastLED +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:HEX. 灯板 +* date:2021/8/17 +******************************************************************************* + Please connect to Port , Control HEX Unit to run rainbow light show. + 请连接端口,控制HEX Unit 彩虹灯显示。 */ + #include #include "FastLED.h" @@ -10,26 +20,23 @@ #define NUM_LEDS 37 CRGB leds[NUM_LEDS]; -uint8_t gHue = 0; +uint8_t gHue = 0; //Initial tone value. 起始色调数值 void setup() { - M5.begin(); - + M5.begin(); //Init M5StickC. 初始化M5StickC M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setTextSize(1); - M5.Lcd.setCursor(7, 5); M5.Lcd.println("HEX Example"); M5.Lcd.setTextColor(WHITE); M5.Lcd.setCursor(0, 40); M5.Lcd.println("Display rainbow effect"); - // Neopixel initialization + // Neopixel initialization. 初始化灯带 FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); - FastLED.setBrightness(10); + FastLED.setBrightness(5); //set the LED brightness to 5. 设置灯带的亮度为5 } void loop(){ - fill_rainbow( leds, NUM_LEDS, gHue, 7); - FastLED.show();// must be executed for neopixel becoming effective - EVERY_N_MILLISECONDS( 20 ) { gHue++; } + fill_rainbow( leds, NUM_LEDS, gHue, 7); //Set the leds from 0 to 37 beads to gradient rainbow color, the starting tone value to gHue, and the color difference between adjacent beads to 7. 将leds灯带的从0~37个灯珠设置为渐变彩虹色,起始色调数值设置为gHue,相邻灯珠色差为7 + FastLED.show(); //Updated LED color. 更新LED色彩 + EVERY_N_MILLISECONDS( 20 ) { gHue++; } //The program is executed every 20 milliseconds. 每20毫秒执行一次其中的程序 } diff --git a/examples/Unit/LCD_ST7789V2/LCD_ST7789V2.ino b/examples/Unit/LCD_ST7789V2/LCD_ST7789V2.ino new file mode 100644 index 0000000..000b30e --- /dev/null +++ b/examples/Unit/LCD_ST7789V2/LCD_ST7789V2.ino @@ -0,0 +1,54 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: LCD. 显示屏 +* date:2021/8/18 +******************************************************************************* + Please connect to Port ,Displays a string on the screen. + 请连接端口,在屏幕上显示字符串。 +*/ +#include + +M5UnitLCD display; + +M5Canvas canvas(&display); + +static constexpr char text[] = "Hello world ! こんにちは世界! this is long long string sample. 寿限無、寿限無、五劫の擦り切れ、海砂利水魚の、水行末・雲来末・風来末、喰う寝る処に住む処、藪ら柑子の藪柑子、パイポ・パイポ・パイポのシューリンガン、シューリンガンのグーリンダイ、グーリンダイのポンポコピーのポンポコナの、長久命の長助"; +static constexpr size_t textlen = sizeof(text) / sizeof(text[0]); +int textpos = 0; +int scrollstep = 2; + +void setup(void) +{ + display.init(); //Initialize the display. 初始化显示屏 + display.setRotation(3); //Rotating display. 旋转显示屏 + canvas.setColorDepth(1); // Set the color depth. 设置色深 + canvas.setFont(&fonts::lgfxJapanMinchoP_32); //Set the font. 设置字体 + canvas.setTextSize(2); //Set the font size. 设置字号 + canvas.createSprite(display.width() + 64, 72); //Create a canvas with a wide display width of +64 and a height of 72. 创建一块宽显示屏宽度+64,高72的画布 +} + +void loop(void) +{ + int32_t cursor_x = canvas.getCursorX() - scrollstep; + if (cursor_x <= 0) + { + textpos = 0; + cursor_x = display.width(); + } + + canvas.setCursor(cursor_x, 0); //Set the cursor position. 设置光标的位置 + canvas.scroll(-scrollstep, 0); //Set the rolling. 设置滚动 + while (textpos < textlen && cursor_x <= display.width()) + { + canvas.print(text[textpos++]); + cursor_x = canvas.getCursorX(); + } + display.waitDisplay(); + canvas.pushSprite(&display, 0, (display.height() - canvas.height()) >> 1); //Displays the contents of the canvas. 显示画布上的内容 +} \ No newline at end of file diff --git a/examples/Unit/Light/Light.ino b/examples/Unit/Light/Light.ino index 6ff5c9e..022bf45 100644 --- a/examples/Unit/Light/Light.ino +++ b/examples/Unit/Light/Light.ino @@ -1,36 +1,36 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Light. 环境光传感器 +* date:2021/8/18 +******************************************************************************* + Please connect to Port A,Use the Light Unit screen to display the current ambient lighting value + 请连接端口 A ,使用Light Unit 屏幕显示当前环境光照值。 +*/ + #include void setup() { - M5.begin(); - //disable the speak noise - //dacWrite(25, 0); - M5.Lcd.setRotation(3); - M5.Lcd.setTextColor(YELLOW); - //M5.Lcd.setTextSize(2); - M5.Lcd.setCursor(20, 2); - M5.Lcd.printf("UNIT_LIGHT EXAMPLE\n"); - pinMode(32, INPUT); - M5.Lcd.setCursor(10, 30); - M5.Lcd.print("GPIO33:"); - M5.Lcd.setCursor(10, 50); - M5.Lcd.print("GPIO32:"); + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.printf("UNIT_LIGHT EXAMPLE\n\n"); + M5.Lcd.println("Analog:"); + M5.Lcd.println("Digital:"); + pinMode(32, INPUT); //Set pin 26 as input mode. 设置引脚26为输入模式 } -uint16_t analogRead_value = 0; -uint16_t digitalRead_value = 0; void loop() { - // put your main code here, to run repeatedly: - M5.Lcd.setCursor(75, 30); - M5.Lcd.setTextColor(BLACK); - M5.Lcd.printf("%d\n", analogRead_value); - M5.Lcd.setCursor(75, 50); - M5.Lcd.printf("%d\n", digitalRead_value); - analogRead_value = analogRead(33); - digitalRead_value = digitalRead(32); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(75, 30); + static uint16_t digitalRead_value = 0, analogRead_value = 0; + analogRead_value = analogRead(33); //Store the analog quantity read from pin 33. 将33号引脚读取到的模拟量存储 + digitalRead_value = digitalRead(32); //Store the number read from pin 32. 将32号引脚读取到的数字量存储 + M5.Lcd.setCursor(50, 15); M5.Lcd.printf("%d\n", analogRead_value); - M5.Lcd.setCursor(75, 50); + M5.Lcd.setCursor(50, 25); M5.Lcd.printf("%d\n", digitalRead_value); delay(10); } diff --git a/examples/Unit/OP90.180_ITR9606/OP90.180_ITR9606.ino b/examples/Unit/OP90.180_ITR9606/OP90.180_ITR9606.ino index 15021b4..b98237a 100644 --- a/examples/Unit/OP90.180_ITR9606/OP90.180_ITR9606.ino +++ b/examples/Unit/OP90.180_ITR9606/OP90.180_ITR9606.ino @@ -1,28 +1,29 @@ /* - Description: Detect the current OP 90/180 Unit Photoelectric switch status. +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:OP 180/90. 非接触式光电限位开关 +* date:2021/8/16 +******************************************************************************* + Please connect to Port,Detect the current OP 90/180 Unit Photoelectric switch status. + 请连接端口,检测当前OP 90/180单元光电开关状态。 */ #include - void setup() { - // put your setup code here, to run once: - M5.begin(); - //Wire.begin(); - M5.Lcd.setCursor(20, 5, 2); - M5.Lcd.println("90/180"); - M5.Lcd.setCursor(20, 20, 2); - M5.Lcd.println("OPTICAL"); - - pinMode(33,INPUT_PULLUP); - + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.setCursor(30, 10); //Set the cursor at (30,10). 将光标设置在(30,10)处 + M5.Lcd.println("90/180 OPTICAL"); + pinMode(33,INPUT_PULLUP); //Set pin 33 to input pull-up mode. 设置33号引脚为输入上拉模式 } void loop() { - // put your main code here, to run repeatedly: - M5.Lcd.setCursor(8, 50, 2); - M5.Lcd.printf("IR receive:"); - M5.Lcd.setCursor(15, 80 ,8); - M5.Lcd.printf("%d",digitalRead(33)); - + M5.Lcd.setCursor(30, 40); + M5.Lcd.printf("IR Receive: %d",digitalRead(33)); //Output the value of pin 33. 输出33号引脚的值 } diff --git a/examples/Unit/PIR/pir.ino b/examples/Unit/PIR/pir.ino index 6607128..589bbe2 100644 --- a/examples/Unit/PIR/pir.ino +++ b/examples/Unit/PIR/pir.ino @@ -1,36 +1,38 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:PIR. 人体红外 +* date:2021/8/11 +******************************************************************************* + Please connect to Port,Human body detection using PIR Unit. + 请连接端口,使用PIR Unit进行人体检测。 +*/ + #include void setup() { - M5.begin(); - M5.Lcd.setRotation(3); - M5.Lcd.setTextColor(YELLOW); - M5.Lcd.setCursor(30, 0); + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 M5.Lcd.println("PIR example"); - Serial.println("PIR example: "); - M5.Lcd.setCursor(65, 10); - M5.Lcd.setTextColor(WHITE); - pinMode(33, INPUT); + M5.Lcd.setCursor(0, 25); //Position the cursor at (0,25). 将光标固定在(0,25) + M5.Lcd.println("Status: \n\nValue: "); + pinMode(33, INPUT); //Set pin 36 to input mode. 设置33号引脚为输入模式 } void loop() { - M5.Lcd.setCursor(0,25); M5.Lcd.print("Status: "); - M5.Lcd.setCursor(0,45); M5.Lcd.print("Value: "); - - M5.Lcd.fillRect(75,25,200,25,BLACK); - M5.Lcd.fillRect(75,45,200,25,BLACK); - - if(digitalRead(33)==1){ - M5.Lcd.setCursor(75, 25);M5.Lcd.print("Sensing"); - M5.Lcd.setCursor(75, 45);M5.Lcd.print("1"); - Serial.println("PIR Status: Sensing"); - Serial.println(" value: 1"); + M5.Lcd.fillRect(45,25,180,50,BLACK); //Draw a black rectangle 180 by 50 at (90,25). 在(90,25)处画一个宽180高50的黑的矩形 + if(digitalRead(33)==1){ //If pin 33 reads a value of 1. 如果33号引脚的读取到的值为1 + M5.Lcd.setCursor(45, 25);M5.Lcd.print("Sensing"); + M5.Lcd.setCursor(45, 45);M5.Lcd.print("1"); } else{ - M5.Lcd.setCursor(75, 25);M5.Lcd.print("Not Sensed"); - M5.Lcd.setCursor(75, 45);M5.Lcd.print("0"); - Serial.println("PIR Status: Not Sensed"); - Serial.println(" value: 0"); + M5.Lcd.setCursor(45, 25);M5.Lcd.print("Not Sensed"); + M5.Lcd.setCursor(45, 45);M5.Lcd.print("0"); } delay(500); - M5.update(); -} +} \ No newline at end of file diff --git a/examples/Unit/RFID_RC522/MFRC522_I2C.cpp b/examples/Unit/RFID_RC522/MFRC522_I2C.cpp new file mode 100644 index 0000000..dbe836a --- /dev/null +++ b/examples/Unit/RFID_RC522/MFRC522_I2C.cpp @@ -0,0 +1,1751 @@ +/* +* MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN +* MFRC522.cpp - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. +* NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. +* Released into the public domain. +* Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library +*/ + +#include +#include "MFRC522_I2C.h" +#include + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for setting up the Arduino +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522( byte chipAddress + //byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + _chipAddress = chipAddress; + // _resetPowerDownPin = resetPowerDownPin; +} // End constructor + + +///////////////////////////////////////////////////////////////////////////////////// +// Basic interface functions for communicating with the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Writes a byte to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte value ///< The value to write. + ) { + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + Wire.write(value); + Wire.endTransmission(); +} // End PCD_WriteRegister() + +/** + * Writes a number of bytes to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte count, ///< The number of bytes to write to the register + byte *values ///< The values to write. Byte array. + ) { + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + for (byte index = 0; index < count; index++) { + Wire.write(values[index]); + } + Wire.endTransmission(); +} // End PCD_WriteRegister() + +/** + * Reads a byte from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +byte MFRC522::PCD_ReadRegister( byte reg ///< The register to read from. One of the PCD_Register enums. + ) { + byte value; + //digitalWrite(_chipSelectPin, LOW); // Select slave + Wire.beginTransmission(_chipAddress); + Wire.write(reg); + Wire.endTransmission(); + + Wire.requestFrom(_chipAddress, 1); + value = Wire.read(); + return value; +} // End PCD_ReadRegister() + +/** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One of the PCD_Register enums. + byte count, ///< The number of bytes to read + byte *values, ///< Byte array to store the values in. + byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. + ) { + if (count == 0) { + return; + } + byte address = reg; + byte index = 0; // Index in values array. + Wire.beginTransmission(_chipAddress); + Wire.write(address); + Wire.endTransmission(); + Wire.requestFrom(_chipAddress, count); + while (Wire.available()) { + if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] + // Create bit mask for bit positions rxAlign..7 + byte mask = 0; + for (byte i = rxAlign; i <= 7; i++) { + mask |= (1 << i); + } + // Read value and tell that we want to read the same address again. + byte value = Wire.read(); + // Apply mask to both current value of values[0] and the new data in value. + values[0] = (values[index] & ~mask) | (value & mask); + } + else { // Normal case + values[index] = Wire.read(); + } + index++; + } +} // End PCD_ReadRegister() + +/** + * Sets the bits given in mask in register reg. + */ +void MFRC522::PCD_SetRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to set. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp | mask); // set bit mask +} // End PCD_SetRegisterBitMask() + +/** + * Clears the bits given in mask from register reg. + */ +void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to clear. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask +} // End PCD_ClearRegisterBitMask() + + +/** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. + byte length, ///< In: The number of bytes to transfer. + byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. + ) { + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO + PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73�s. + word i = 5000; + byte n; + while (1) { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) { // CRCIRq bit set - calculation done + break; + } + if (--i == 0) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // Transfer the result from the registers to the result buffer + result[0] = PCD_ReadRegister(CRCResultRegL); + result[1] = PCD_ReadRegister(CRCResultRegH); + return STATUS_OK; +} // End PCD_CalculateCRC() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for manipulating the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init() { + // Set the chipSelectPin as digital output, do not select the slave yet + + // Set the resetPowerDownPin as digital output, do not reset or power down. + // pinMode(_resetPowerDownPin, OUTPUT); + + + // if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. + // digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. + // // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + // delay(50); + // } + // else { // Perform a soft reset + PCD_Reset(); + // } + + // When communicating with a PICC we need a timeout if something goes wrong. + // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. + // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. + PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TReloadRegL, 0xE8); + + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) +} // End PCD_Init() + +/** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ +void MFRC522::PCD_Reset() { + PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. + // The datasheet does not mention how long the SoftRest command takes to complete. + // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + delay(50); + // Wait for the PowerDown bit in CommandReg to be cleared + while (PCD_ReadRegister(CommandReg) & (1<<4)) { + // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. + } +} // End PCD_Reset() + +/** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins are disabled. + */ +void MFRC522::PCD_AntennaOn() { + byte value = PCD_ReadRegister(TxControlReg); + if ((value & 0x03) != 0x03) { + PCD_WriteRegister(TxControlReg, value | 0x03); + } +} // End PCD_AntennaOn() + +/** + * Turns the antenna off by disabling pins TX1 and TX2. + */ +void MFRC522::PCD_AntennaOff() { + PCD_ClearRegisterBitMask(TxControlReg, 0x03); +} // End PCD_AntennaOff() + +/** + * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + * + * @return Value of the RxGain, scrubbed to the 3 bits used. + */ +byte MFRC522::PCD_GetAntennaGain() { + return PCD_ReadRegister(RFCfgReg) & (0x07<<4); +} // End PCD_GetAntennaGain() + +/** + * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + */ +void MFRC522::PCD_SetAntennaGain(byte mask) { + if (PCD_GetAntennaGain() != mask) { // only bother if there is a change + PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern + PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits + } +} // End PCD_SetAntennaGain() + +/** + * Performs a self-test of the MFRC522 + * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * @return Whether or not the test passed. + */ +bool MFRC522::PCD_PerformSelfTest() { + // This follows directly the steps outlined in 16.1.1 + // 1. Perform a soft reset. + PCD_Reset(); + + // 2. Clear the internal buffer by writing 25 bytes of 00h + byte ZEROES[25] = {0x00}; + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // flush the FIFO buffer + PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO + PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer + + // 3. Enable self-test + PCD_WriteRegister(AutoTestReg, 0x09); + + // 4. Write 00h to FIFO buffer + PCD_WriteRegister(FIFODataReg, 0x00); + + // 5. Start self-test by issuing the CalcCRC command + PCD_WriteRegister(CommandReg, PCD_CalcCRC); + + // 6. Wait for self-test to complete + word i; + byte n; + for (i = 0; i < 0xFF; i++) { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) { // CRCIRq bit set - calculation done + break; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // 7. Read out resulting 64 bytes from the FIFO buffer. + byte result[64]; + PCD_ReadRegister(FIFODataReg, 64, result, 0); + + // Auto self-test done + // Reset AutoTestReg register to be 0 again. Required for normal operation. + PCD_WriteRegister(AutoTestReg, 0x00); + + // Determine firmware version (see section 9.3.4.8 in spec) + byte version = PCD_ReadRegister(VersionReg); + + // Pick the appropriate reference values + const byte *reference; + switch (version) { + case 0x88: // Fudan Semiconductor FM17522 clone + reference = FM17522_firmware_reference; + break; + case 0x90: // Version 0.0 + reference = MFRC522_firmware_referenceV0_0; + break; + case 0x91: // Version 1.0 + reference = MFRC522_firmware_referenceV1_0; + break; + case 0x92: // Version 2.0 + reference = MFRC522_firmware_referenceV2_0; + break; + default: // Unknown version + return false; + } + + // Verify that the results match up to our expectations + for (i = 0; i < 64; i++) { + if (result[i] != pgm_read_byte(&(reference[i]))) { + return false; + } + } + + // Test passed; all is good. + return true; +} // End PCD_PerformSelfTest() + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte waitIRq = 0x30; // RxIRq and IdleIRq + return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); +} // End PCD_TransceiveData() + +/** + * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. + byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. + byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte n, _validBits; + unsigned int i; + + // Prepare values for BitFramingReg + byte txLastBits = validBits ? *validBits : 0; + byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO + PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments + PCD_WriteRegister(CommandReg, command); // Execute the command + if (command == PCD_Transceive) { + PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts + } + + // Wait for the command to complete. + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. + // Each iteration of the do-while-loop takes 17.86�s. + i = 2000; + while (1) { + n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + if (n & waitIRq) { // One of the interrupts that signal success has been set. + break; + } + if (n & 0x01) { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + if (--i == 0) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + + // Stop now if any errors except collisions were detected. + byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr + if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr + return STATUS_ERROR; + } + + // If the caller wants data back, get it from the MFRC522. + if (backData && backLen) { + n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO + if (n > *backLen) { + return STATUS_NO_ROOM; + } + *backLen = n; // Number of bytes returned + PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO + _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. + if (validBits) { + *validBits = _validBits; + } + } + + // Tell about collisions + if (errorRegValue & 0x08) { // CollErr + return STATUS_COLLISION; + } + + // Perform CRC_A validation if requested. + if (backData && backLen && checkCRC) { + // In this case a MIFARE Classic NAK is not OK. + if (*backLen == 1 && _validBits == 4) { + return STATUS_MIFARE_NACK; + } + // We need at least the CRC_A value and all 8 bits of the last byte must be received. + if (*backLen < 2 || _validBits != 0) { + return STATUS_CRC_WRONG; + } + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + byte controlBuffer[2]; + n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (n != STATUS_OK) { + return n; + } + if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { + return STATUS_CRC_WRONG; + } + } + + return STATUS_OK; +} // End PCD_CommunicateWithPICC() + +/** + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); +} // End PICC_RequestA() + +/** + * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); +} // End PICC_WakeupA() + +/** + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + byte validBits; + byte status; + + if (bufferATQA == NULL || *bufferSize < 2) { // The ATQA response is 2 bytes long. + return STATUS_NO_ROOM; + } + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); + if (status != STATUS_OK) { + return status; + } + if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. + return STATUS_ERROR; + } + return STATUS_OK; +} // End PICC_REQA_or_WUPA() + +/** + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). + * On success: + * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) + * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. + * + * A PICC UID consists of 4, 7 or 10 bytes. + * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: + * UID size Number of UID bytes Cascade levels Example of PICC + * ======== =================== ============== =============== + * single 4 1 MIFARE Classic + * double 7 2 MIFARE Ultralight + * triple 10 3 Not currently in use? + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. + ) { + bool uidComplete; + bool selectDone; + bool useCascadeTag; + byte cascadeLevel = 1; + byte result; + byte count; + byte index; + byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A + byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. + byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. + byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. + byte *responseBuffer; + byte responseLength; + + // Description of buffer structure: + // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 + // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. + // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. + // Byte 3: UID-data + // Byte 4: UID-data + // Byte 5: UID-data + // Byte 6: BCC Block Check Character - XOR of bytes 2-5 + // Byte 7: CRC_A + // Byte 8: CRC_A + // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. + // + // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) + // UID size Cascade level Byte2 Byte3 Byte4 Byte5 + // ======== ============= ===== ===== ===== ===== + // 4 bytes 1 uid0 uid1 uid2 uid3 + // 7 bytes 1 CT uid0 uid1 uid2 + // 2 uid3 uid4 uid5 uid6 + // 10 bytes 1 CT uid0 uid1 uid2 + // 2 CT uid3 uid4 uid5 + // 3 uid6 uid7 uid8 uid9 + + // Sanity checks + if (validBits > 80) { + return STATUS_INVALID; + } + + // Prepare MFRC522 + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + + // Repeat Cascade Level loop until we have a complete UID. + uidComplete = false; + while (!uidComplete) { + // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. + switch (cascadeLevel) { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uidIndex = 0; + useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uidIndex = 3; + useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes + break; + + case 3: + buffer[0] = PICC_CMD_SEL_CL3; + uidIndex = 6; + useCascadeTag = false; // Never used in CL3. + break; + + default: + return STATUS_INTERNAL_ERROR; + break; + } + + // How many UID bits are known in this Cascade Level? + currentLevelKnownBits = validBits - (8 * uidIndex); + if (currentLevelKnownBits < 0) { + currentLevelKnownBits = 0; + } + // Copy the known bits from uid->uidByte[] to buffer[] + index = 2; // destination index in buffer[] + if (useCascadeTag) { + buffer[index++] = PICC_CMD_CT; + } + byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. + if (bytesToCopy) { + byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag + if (bytesToCopy > maxBytes) { + bytesToCopy = maxBytes; + } + for (count = 0; count < bytesToCopy; count++) { + buffer[index++] = uid->uidByte[uidIndex + count]; + } + } + // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits + if (useCascadeTag) { + currentLevelKnownBits += 8; + } + + // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. + selectDone = false; + while (!selectDone) { + // Find out how many bits and bytes to send and receive. + if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. + //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes + // Calculate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 7, &buffer[7]); + if (result != STATUS_OK) { + return result; + } + txLastBits = 0; // 0 => All 8 bits are valid. + bufferUsed = 9; + // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) + responseBuffer = &buffer[6]; + responseLength = 3; + } + else { // This is an ANTICOLLISION. + //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + txLastBits = currentLevelKnownBits % 8; + count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. + index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs + buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits + bufferUsed = index + (txLastBits ? 1 : 0); + // Store response in the unused part of buffer + responseBuffer = &buffer[index]; + responseLength = sizeof(buffer) - index; + } + + // Set bit adjustments + rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. + PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + // Transmit the buffer and receive the response. + result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); + if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. + result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (result & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. + if (collisionPos == 0) { + collisionPos = 32; + } + if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen + return STATUS_INTERNAL_ERROR; + } + // Choose the PICC with the bit set. + currentLevelKnownBits = collisionPos; + count = (currentLevelKnownBits - 1) % 8; // The bit to modify + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << count); + } + else if (result != STATUS_OK) { + return result; + } + else { // STATUS_OK + if (currentLevelKnownBits >= 32) { // This was a SELECT. + selectDone = true; // No more anticollision + // We continue below outside the while. + } + else { // This was an ANTICOLLISION. + // We now have all 32 bits of the UID in this Cascade Level + currentLevelKnownBits = 32; + // Run loop again to do the SELECT. + } + } + } // End of while (!selectDone) + + // We do not check the CBB - it was constructed by us above. + + // Copy the found UID bytes from buffer[] to uid->uidByte[] + index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] + bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; + for (count = 0; count < bytesToCopy; count++) { + uid->uidByte[uidIndex + count] = buffer[index++]; + } + + // Check response SAK (Select Acknowledge) + if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). + return STATUS_ERROR; + } + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. + result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { + return STATUS_CRC_WRONG; + } + if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes + cascadeLevel++; + } + else { + uidComplete = true; + uid->sak = responseBuffer[0]; + } + } // End of while (!uidComplete) + + // Set correct uid->size + uid->size = 3 * cascadeLevel + 1; + + return STATUS_OK; +} // End PICC_Select() + +/** + * Instructs a PICC in state ACTIVE(*) to go to state HALT. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_HaltA() { + byte result; + byte buffer[4]; + + // Build command buffer + buffer[0] = PICC_CMD_HLTA; + buffer[1] = 0; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Send the command. + // The standard says: + // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the + // HLTA command, this response shall be interpreted as 'not acknowledge'. + // We interpret that this way: Only STATUS_TIMEOUT is an success. + result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); + if (result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) + return STATUS_ERROR; + } + return result; +} // End PICC_HaltA() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with MIFARE PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the MFRC522 MFAuthent command. + * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. + * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. + * For use with MIFARE Classic PICCs. + * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. + * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. + * + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * + * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. + */ +byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + byte blockAddr, ///< The block number. See numbering in the comments in the .h file. + MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) + Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. + ) { + byte waitIRq = 0x10; // IdleIRq + + // Build command buffer + byte sendData[12]; + sendData[0] = command; + sendData[1] = blockAddr; + for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes + sendData[2+i] = key->keyByte[i]; + } + for (byte i = 0; i < 4; i++) { // The first 4 bytes of the UID + sendData[8+i] = uid->uidByte[i]; + } + + // Start the authentication. + return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); +} // End PCD_Authenticate() + +/** + * Used to exit the PCD from its authenticated state. + * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. + */ +void MFRC522::PCD_StopCrypto1() { + // Clear MFCrypto1On bit + PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] +} // End PCD_StopCrypto1() + +/** + * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. + * The MF0ICU1 returns a NAK for higher addresses. + * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. + * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. + * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. + * + * The buffer must be at least 18 bytes because a CRC_A is also returned. + * Checks the CRC_A before returning STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + byte *buffer, ///< The buffer to store the data in + byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. + ) { + byte result; + + // Sanity check + if (buffer == NULL || *bufferSize < 18) { + return STATUS_NO_ROOM; + } + + // Build command buffer + buffer[0] = PICC_CMD_MF_READ; + buffer[1] = blockAddr; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); +} // End MIFARE_Read() + +/** + * Writes 16 bytes to the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". + * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) + * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. + * * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + byte *buffer, ///< The 16 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 16) { + return STATUS_INVALID; + } + + // Mifare Classic protocol requires two communications to perform a write. + // Step 1: Tell the PICC we want to write to block blockAddr. + byte cmdBuffer[2]; + cmdBuffer[0] = PICC_CMD_MF_WRITE; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_Write() + +/** + * Writes a 4 byte page to the active MIFARE Ultralight PICC. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. + byte *buffer, ///< The 4 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 4) { + return STATUS_INVALID; + } + + // Build commmand buffer + byte cmdBuffer[6]; + cmdBuffer[0] = PICC_CMD_UL_WRITE; + cmdBuffer[1] = page; + memcpy(&cmdBuffer[2], buffer, 4); + + // Perform the write + result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Ultralight_Write() + +/** + * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is subtracted from the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); +} // End MIFARE_Decrement() + +/** + * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is added to the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); +} // End MIFARE_Increment() + +/** + * MIFARE Restore copies the value of the addressed block into a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. + ) { + // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. + // Doing only a single step does not work, so I chose to transfer 0L in step two. + return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); +} // End MIFARE_Restore() + +/** + * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use + byte blockAddr, ///< The block (0-0xff) number. + long data ///< The data to transfer in step 2 + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Step 1: Tell the PICC the command and block address + cmdBuffer[0] = command; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_TwoStepHelper() + +/** + * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Tell the PICC we want to transfer the result into block blockAddr. + cmdBuffer[0] = PICC_CMD_MF_TRANSFER; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Transfer() + +/** + * Helper routine to read the current value from a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[out] value Current value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { + byte status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Read the block + status = MIFARE_Read(blockAddr, buffer, &size); + if (status == STATUS_OK) { + // Extract the value + *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + } + return status; +} // End MIFARE_GetValue() + +/** + * Helper routine to write a specific value into a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[in] value New value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { + byte buffer[18]; + + // Translate the long into 4 bytes; repeated 2x in value block + buffer[0] = buffer[ 8] = (value & 0xFF); + buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; + buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; + buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; + // Inverse 4 bytes also found in value block + buffer[4] = ~buffer[0]; + buffer[5] = ~buffer[1]; + buffer[6] = ~buffer[2]; + buffer[7] = ~buffer[3]; + // Address 2x with inverse address 2x + buffer[12] = buffer[14] = blockAddr; + buffer[13] = buffer[15] = ~blockAddr; + + // Write the whole data block + return MIFARE_Write(blockAddr, buffer, 16); +} // End MIFARE_SetValue() + +///////////////////////////////////////////////////////////////////////////////////// +// Support functions +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Wrapper for MIFARE protocol communication. + * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + byte sendLen, ///< Number of bytes in sendData. + bool acceptTimeout ///< True => A timeout is also success + ) { + byte result; + byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + // Sanity check + if (sendData == NULL || sendLen > 16) { + return STATUS_INVALID; + } + + // Copy sendData[] to cmdBuffer[] and add CRC_A + memcpy(cmdBuffer, sendData, sendLen); + result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); + if (result != STATUS_OK) { + return result; + } + sendLen += 2; + + // Transceive the data, store the reply in cmdBuffer[] + byte waitIRq = 0x30; // RxIRq and IdleIRq + byte cmdBufferSize = sizeof(cmdBuffer); + byte validBits = 0; + result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); + if (acceptTimeout && result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result != STATUS_OK) { + return result; + } + // The PICC must reply with a 4 bit ACK + if (cmdBufferSize != 1 || validBits != 4) { + return STATUS_ERROR; + } + if (cmdBuffer[0] != MF_ACK) { + return STATUS_MIFARE_NACK; + } + return STATUS_OK; +} // End PCD_MIFARE_Transceive() + +/** + * Returns a __FlashStringHelper pointer to a status code name. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. + ) { + switch (code) { + case STATUS_OK: return F("Success."); break; + case STATUS_ERROR: return F("Error in communication."); break; + case STATUS_COLLISION: return F("Collission detected."); break; + case STATUS_TIMEOUT: return F("Timeout in communication."); break; + case STATUS_NO_ROOM: return F("A buffer is not big enough."); break; + case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); break; + case STATUS_INVALID: return F("Invalid argument."); break; + case STATUS_CRC_WRONG: return F("The CRC_A does not match."); break; + case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); break; + default: return F("Unknown error"); break; + } +} // End GetStatusCodeName() + +/** + * Translates the SAK (Select Acknowledge) to a PICC type. + * + * @return PICC_Type + */ +byte MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). + ) { + if (sak & 0x04) { // UID not complete + return PICC_TYPE_NOT_COMPLETE; + } + + switch (sak) { + case 0x09: return PICC_TYPE_MIFARE_MINI; break; + case 0x08: return PICC_TYPE_MIFARE_1K; break; + case 0x18: return PICC_TYPE_MIFARE_4K; break; + case 0x00: return PICC_TYPE_MIFARE_UL; break; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; break; + case 0x01: return PICC_TYPE_TNP3XXX; break; + default: break; + } + + if (sak & 0x20) { + return PICC_TYPE_ISO_14443_4; + } + + if (sak & 0x40) { + return PICC_TYPE_ISO_18092; + } + + return PICC_TYPE_UNKNOWN; +} // End PICC_GetType() + +/** + * Returns a __FlashStringHelper pointer to the PICC type name. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. + ) { + switch (piccType) { + case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); break; + case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");break; + case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); break; + case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); break; + case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); break; + case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); break; + case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); break; + case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); break; + case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); break; + case PICC_TYPE_UNKNOWN: + default: return F("Unknown type"); break; + } +} // End PICC_GetTypeName() + +/** + * Dumps debug info about the selected PICC to Serial. + * On success the PICC is halted after dumping the data. + * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + */ +void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). + ) { + MIFARE_Key key; + + // UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < uid->size; i++) { + if(uid->uidByte[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(uid->uidByte[i], HEX); + } + Serial.println(); + + // PICC type + byte piccType = PICC_GetType(uid->sak); + Serial.print(F("PICC type: ")); + Serial.println(PICC_GetTypeName(piccType)); + + // Dump contents + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + case PICC_TYPE_MIFARE_1K: + case PICC_TYPE_MIFARE_4K: + // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + PICC_DumpMifareClassicToSerial(uid, piccType, &key); + break; + + case PICC_TYPE_MIFARE_UL: + PICC_DumpMifareUltralightToSerial(); + break; + + case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} // End PICC_DumpToSerial() + +/** + * Dumps memory contents of a MIFARE Classic PICC. + * On success the PICC is halted after dumping the data. + */ +void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + byte piccType, ///< One of the PICC_Type enums. + MIFARE_Key *key ///< Key A used for all sectors. + ) { + byte no_of_sectors = 0; + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + no_of_sectors = 5; + break; + + case PICC_TYPE_MIFARE_1K: + // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + no_of_sectors = 16; + break; + + case PICC_TYPE_MIFARE_4K: + // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + no_of_sectors = 40; + break; + + default: // Should not happen. Ignore. + break; + } + + // Dump sectors, highest address first. + if (no_of_sectors) { + Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); + for (int8_t i = no_of_sectors - 1; i >= 0; i--) { + PICC_DumpMifareClassicSectorToSerial(uid, key, i); + } + } + PICC_HaltA(); // Halt the PICC before stopping the encrypted session. + PCD_StopCrypto1(); +} // End PICC_DumpMifareClassicToSerial() + +/** + * Dumps memory contents of a sector of a MIFARE Classic PICC. + * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. + * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. + */ +void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + MIFARE_Key *key, ///< Key A for the sector. + byte sector ///< The sector to dump, 0..39. + ) { + byte status; + byte firstBlock; // Address of lowest address to dump actually last block dumped) + byte no_of_blocks; // Number of blocks in sector + bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. + + // The access bits are stored in a peculiar fashion. + // There are four groups: + // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. + // The four CX bits are stored together in a nible cx and an inverted nible cx_. + byte c1, c2, c3; // Nibbles + byte c1_, c2_, c3_; // Inverted nibbles + bool invertedError; // True if one of the inverted nibbles did not match + byte g[4]; // Access bits for each of the four groups. + byte group; // 0-3 - active group for access bits + bool firstInGroup; // True for the first block dumped in the group + + // Determine position and size of sector. + if (sector < 32) { // Sectors 0..31 has 4 blocks each + no_of_blocks = 4; + firstBlock = sector * no_of_blocks; + } + else if (sector < 40) { // Sectors 32-39 has 16 blocks each + no_of_blocks = 16; + firstBlock = 128 + (sector - 32) * no_of_blocks; + } + else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. + return; + } + + // Dump blocks, highest address first. + byte byteCount; + byte buffer[18]; + byte blockAddr; + isSectorTrailer = true; + for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { + blockAddr = firstBlock + blockOffset; + // Sector number - only on first line + if (isSectorTrailer) { + if(sector < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(sector); + Serial.print(F(" ")); + } + else { + Serial.print(F(" ")); + } + // Block number + if(blockAddr < 10) + Serial.print(F(" ")); // Pad with spaces + else { + if(blockAddr < 100) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + } + Serial.print(blockAddr); + Serial.print(F(" ")); + // Establish encrypted communications before reading the first block + if (isSectorTrailer) { + status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); + if (status != STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + return; + } + } + // Read block + byteCount = sizeof(buffer); + status = MIFARE_Read(blockAddr, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + continue; + } + // Dump data + for (byte index = 0; index < 16; index++) { + if(buffer[index] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[index], HEX); + if ((index % 4) == 3) { + Serial.print(F(" ")); + } + } + // Parse sector trailer data + if (isSectorTrailer) { + c1 = buffer[7] >> 4; + c2 = buffer[8] & 0xF; + c3 = buffer[8] >> 4; + c1_ = buffer[6] & 0xF; + c2_ = buffer[6] >> 4; + c3_ = buffer[7] & 0xF; + invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); + g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); + g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); + g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); + g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); + isSectorTrailer = false; + } + + // Which access group is this block in? + if (no_of_blocks == 4) { + group = blockOffset; + firstInGroup = true; + } + else { + group = blockOffset / 5; + firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); + } + + if (firstInGroup) { + // Print access bits + Serial.print(F(" [ ")); + Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 0) & 1, DEC); + Serial.print(F(" ] ")); + if (invertedError) { + Serial.print(F(" Inverted access bits did not match! ")); + } + } + + if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block + long value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + Serial.print(F(" Value=0x")); Serial.print(value, HEX); + Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); + } + Serial.println(); + } + + return; +} // End PICC_DumpMifareClassicSectorToSerial() + +/** + * Dumps memory contents of a MIFARE Ultralight PICC. + */ +void MFRC522::PICC_DumpMifareUltralightToSerial() { + byte status; + byte byteCount; + byte buffer[18]; + byte i; + + Serial.println(F("Page 0 1 2 3")); + // Try the mpages of the original Ultralight. Ultralight C has more pages. + for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + // Read pages + byteCount = sizeof(buffer); + status = MIFARE_Read(page, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + break; + } + // Dump data + for (byte offset = 0; offset < 4; offset++) { + i = page + offset; + if(i < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(i); + Serial.print(F(" ")); + for (byte index = 0; index < 4; index++) { + i = 4 * offset + index; + if(buffer[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[i], HEX); + } + Serial.println(); + } + } +} // End PICC_DumpMifareUltralightToSerial() + +/** + * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1). + */ +void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + ) { + byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); + byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); + byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); + + accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); + accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); + accessBitBuffer[2] = c3 << 4 | c2; +} // End MIFARE_SetAccessBits() + + +/** + * Performs the "magic sequence" needed to get Chinese UID changeable + * Mifare cards to allow writing to sector 0, where the card UID is stored. + * + * Note that you do not need to have selected the card through REQA or WUPA, + * this sequence works immediately when the card is in the reader vicinity. + * This means you can use this method even on "bricked" cards that your reader does + * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). + * + * Of course with non-bricked devices, you're free to select them before calling this function. + */ +bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { + // Magic sequence: + // > 50 00 57 CD (HALT + CRC) + // > 40 (7 bits only) + // < A (4 bits only) + // > 43 + // < A (4 bits only) + // Then you can write to sector 0 without authenticating + + PICC_HaltA(); // 50 00 57 CD + + byte cmd = 0x40; + byte validBits = 7; /* Our command is only 7 bits. After receiving card response, + this will contain amount of valid response bits. */ + byte response[32]; // Card's response is written here + byte received; + byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x40 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + cmd = 0x43; + validBits = 8; + status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x43 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + // You can now write to sector 0 without authenticating! + return true; +} // End MIFARE_OpenUidBackdoor() + +/** + * Reads entire block 0, including all manufacturer data, and overwrites + * that block with the new UID, a freshly calculated BCC, and the original + * manufacturer data. + * + * It assumes a default KEY A of 0xFFFFFFFFFFFF. + * Make sure to have selected the card before this function is called. + */ +bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { + + // UID + BCC byte can not be larger than 16 together + if (!newUid || !uidSize || uidSize > 15) { + if (logErrors) { + Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); + } + return false; + } + + // Authenticate for reading + MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + + if (status == STATUS_TIMEOUT) { + // We get a read timeout if no card is selected yet, so let's select one + + // Wake the card up again if sleeping +// byte atqa_answer[2]; +// byte atqa_size = 2; +// PICC_WakeupA(atqa_answer, &atqa_size); + + if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { + Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); + return false; + } + + status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + // We tried, time to give up + if (logErrors) { + Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + else { + if (logErrors) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + + // Read block 0 + byte block0_buffer[18]; + byte byteCount = sizeof(block0_buffer); + status = MIFARE_Read((byte)0, block0_buffer, &byteCount); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); + } + return false; + } + + // Write new UID to the data we just read, and calculate BCC byte + byte bcc = 0; + for (int i = 0; i < uidSize; i++) { + block0_buffer[i] = newUid[i]; + bcc ^= newUid[i]; + } + + // Write BCC byte to buffer + block0_buffer[uidSize] = bcc; + + // Stop encrypted traffic so we can send raw bytes + PCD_StopCrypto1(); + + // Activate UID backdoor + if (!MIFARE_OpenUidBackdoor(logErrors)) { + if (logErrors) { + Serial.println(F("Activating the UID backdoor failed.")); + } + return false; + } + + // Write modified block 0 back to card + status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + + // Wake the card up again + byte atqa_answer[2]; + byte atqa_size = 2; + PICC_WakeupA(atqa_answer, &atqa_size); + + return true; +} + +/** + * Resets entire sector 0 to zeroes, so the card can be read again by readers. + */ +bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { + MIFARE_OpenUidBackdoor(logErrors); + + byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Write modified block 0 back to card + byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + return true; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Convenience functions - does not add extra functionality +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Returns true if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + * + * @return bool + */ +bool MFRC522::PICC_IsNewCardPresent() { + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + byte result = PICC_RequestA(bufferATQA, &bufferSize); + return (result == STATUS_OK || result == STATUS_COLLISION); +} // End PICC_IsNewCardPresent() + +/** + * Simple wrapper around PICC_Select. + * Returns true if a UID could be read. + * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. + * The read UID is available in the class variable uid. + * + * @return bool + */ +bool MFRC522::PICC_ReadCardSerial() { + byte result = PICC_Select(&uid); + return (result == STATUS_OK); +} // End PICC_ReadCardSerial() diff --git a/examples/Unit/RFID_RC522/MFRC522_I2C.h b/examples/Unit/RFID_RC522/MFRC522_I2C.h new file mode 100644 index 0000000..a58a9df --- /dev/null +++ b/examples/Unit/RFID_RC522/MFRC522_I2C.h @@ -0,0 +1,407 @@ +/** + * MFRC522_I2C.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN + * MFRC522_I2C.h - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT. + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. + * Extended by Ahmet Remzi Ozcan with I2C functionality. + * Author: arozcan @ https://github.com/arozcan/MFRC522-I2C-Library + * Released into the public domain. + * + * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. + * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board. + * + * There are three hardware components involved: + * 1) The micro controller: An Arduino + * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * + * The microcontroller and card reader uses I2C for communication. + * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * The card reader and the tags communicate using a 13.56MHz electromagnetic field. + * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision". + * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf + * Details are found in chapter 6, Type A – Initialization and anticollision. + * + * If only the PICC UID is wanted, the above documents has all the needed information. + * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. + * The MIFARE Classic chips and protocol is described in the datasheets: + * 1K: http://www.nxp.com/documents/data_sheet/MF1S503x.pdf + * 4K: http://www.nxp.com/documents/data_sheet/MF1S703x.pdf + * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf + * The MIFARE Ultralight chip and protocol is described in the datasheets: + * Ultralight: http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + * Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf + * + * MIFARE Classic 1K (MF1S503x): + * Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + * The blocks are numbered 0-63. + * Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + * Bytes 0-5: Key A + * Bytes 6-8: Access Bits + * Bytes 9: User data + * Bytes 10-15: Key B (or user data) + * Block 0 is read-only manufacturer data. + * To access a block, an authentication using a key from the block's sector must be performed first. + * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. + * To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. + * MIFARE Classic 4K (MF1S703x): + * Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + * The blocks are numbered 0-255. + * The last block in each sector is the Sector Trailer like above. + * MIFARE Classic Mini (MF1 IC S20): + * Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + * The blocks are numbered 0-19. + * The last block in each sector is the Sector Trailer like above. + * + * MIFARE Ultralight (MF0ICU1): + * Has 16 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. + * MIFARE Ultralight C (MF0ICU2): + * Has 48 pages of 4 bytes = 192 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. + * Page 40 Lock bytes + * Page 41 16 bit one way counter + * Pages 42-43 Authentication configuration + * Pages 44-47 Authentication key + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include +#include + +// Firmware data for self-test +// Reference values based on firmware version +// Hint: if needed, you can remove unused self-test data to save flash memory +// +// Version 0.0 (0x90) +// Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 Sefttest +const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { + 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, + 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, + 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, + 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, + 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, + 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, + 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, + 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D +}; +// Version 1.0 (0x91) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { + 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, + 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, + 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, + 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, + 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, + 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, + 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, + 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 +}; +// Version 2.0 (0x92) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { + 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, + 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, + 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, + 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, + 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, + 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, + 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, + 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F +}; +// Clone +// Fudan Semiconductor FM17522 (0x88) +const byte FM17522_firmware_reference[] PROGMEM = { + 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, + 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, + 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, + 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, + 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, + 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, + 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, + 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 +}; + +class MFRC522 { +public: + // MFRC522 registers. Described in chapter 9 of the datasheet. + enum PCD_Register { + // Page 0: Command and status + // 0x00 // reserved for future use + CommandReg = 0x01 , // starts and stops command execution + ComIEnReg = 0x02 , // enable and disable interrupt request control bits + DivIEnReg = 0x03 , // enable and disable interrupt request control bits + ComIrqReg = 0x04 , // interrupt request bits + DivIrqReg = 0x05 , // interrupt request bits + ErrorReg = 0x06 , // error bits showing the error status of the last command executed + Status1Reg = 0x07 , // communication status bits + Status2Reg = 0x08 , // receiver and transmitter status bits + FIFODataReg = 0x09 , // input and output of 64 byte FIFO buffer + FIFOLevelReg = 0x0A , // number of bytes stored in the FIFO buffer + WaterLevelReg = 0x0B , // level for FIFO underflow and overflow warning + ControlReg = 0x0C , // miscellaneous control registers + BitFramingReg = 0x0D , // adjustments for bit-oriented frames + CollReg = 0x0E , // bit position of the first bit-collision detected on the RF interface + // 0x0F // reserved for future use + + // Page 1: Command + // 0x10 // reserved for future use + ModeReg = 0x11 , // defines general modes for transmitting and receiving + TxModeReg = 0x12 , // defines transmission data rate and framing + RxModeReg = 0x13 , // defines reception data rate and framing + TxControlReg = 0x14 , // controls the logical behavior of the antenna driver pins TX1 and TX2 + TxASKReg = 0x15 , // controls the setting of the transmission modulation + TxSelReg = 0x16 , // selects the internal sources for the antenna driver + RxSelReg = 0x17 , // selects internal receiver settings + RxThresholdReg = 0x18 , // selects thresholds for the bit decoder + DemodReg = 0x19 , // defines demodulator settings + // 0x1A // reserved for future use + // 0x1B // reserved for future use + MfTxReg = 0x1C , // controls some MIFARE communication transmit parameters + MfRxReg = 0x1D , // controls some MIFARE communication receive parameters + // 0x1E // reserved for future use + SerialSpeedReg = 0x1F , // selects the speed of the serial UART interface + + // Page 2: Configuration + // 0x20 // reserved for future use + CRCResultRegH = 0x21 , // shows the MSB and LSB values of the CRC calculation + CRCResultRegL = 0x22 , + // 0x23 // reserved for future use + ModWidthReg = 0x24 , // controls the ModWidth setting? + // 0x25 // reserved for future use + RFCfgReg = 0x26 , // configures the receiver gain + GsNReg = 0x27 , // selects the conductance of the antenna driver pins TX1 and TX2 for modulation + CWGsPReg = 0x28 , // defines the conductance of the p-driver output during periods of no modulation + ModGsPReg = 0x29 , // defines the conductance of the p-driver output during periods of modulation + TModeReg = 0x2A , // defines settings for the internal timer + TPrescalerReg = 0x2B , // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. + TReloadRegH = 0x2C , // defines the 16-bit timer reload value + TReloadRegL = 0x2D , + TCounterValueRegH = 0x2E , // shows the 16-bit timer value + TCounterValueRegL = 0x2F , + + // Page 3: Test Registers + // 0x30 // reserved for future use + TestSel1Reg = 0x31 , // general test signal configuration + TestSel2Reg = 0x32 , // general test signal configuration + TestPinEnReg = 0x33 , // enables pin output driver on pins D1 to D7 + TestPinValueReg = 0x34 , // defines the values for D1 to D7 when it is used as an I/O bus + TestBusReg = 0x35 , // shows the status of the internal test bus + AutoTestReg = 0x36 , // controls the digital self test + VersionReg = 0x37 , // shows the software version + AnalogTestReg = 0x38 , // controls the pins AUX1 and AUX2 + TestDAC1Reg = 0x39 , // defines the test value for TestDAC1 + TestDAC2Reg = 0x3A , // defines the test value for TestDAC2 + TestADCReg = 0x3B // shows the value of ADC I and Q channels + // 0x3C // reserved for production tests + // 0x3D // reserved for production tests + // 0x3E // reserved for production tests + // 0x3F // reserved for production tests + }; + + // MFRC522 commands. Described in chapter 10 of the datasheet. + enum PCD_Command { + PCD_Idle = 0x00, // no action, cancels current command execution + PCD_Mem = 0x01, // stores 25 bytes into the internal buffer + PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number + PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test + PCD_Transmit = 0x04, // transmits data from the FIFO buffer + PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit + PCD_Receive = 0x08, // activates the receiver circuits + PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission + PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader + PCD_SoftReset = 0x0F // resets the MFRC522 + }; + + // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). + // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf + enum PCD_RxGain { + RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum + RxGain_23dB = 0x01 << 4, // 001b - 23 dB + RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b + RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b + RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default + RxGain_38dB = 0x05 << 4, // 101b - 38 dB + RxGain_43dB = 0x06 << 4, // 110b - 43 dB + RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum + RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB + RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB + RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB + }; + + // Commands sent to the PICC. + enum PICC_Command { + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. + PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 + PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) + // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. + // The read/write commands can also be used for MIFARE Ultralight. + PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A + PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B + PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. + PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. + PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. + // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. + PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. + }; + + // MIFARE constants that does not fit anywhere else + enum MIFARE_Misc { + MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. + MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. + }; + + // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. + enum PICC_Type { + PICC_TYPE_UNKNOWN = 0, + PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 + PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) + PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes + PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB + PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB + PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C + PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus + PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure + PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + enum StatusCode { + STATUS_OK = 1, // Success + STATUS_ERROR = 2, // Error in communication + STATUS_COLLISION = 3, // Collission detected + STATUS_TIMEOUT = 4, // Timeout in communication. + STATUS_NO_ROOM = 5, // A buffer is not big enough. + STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) + STATUS_INVALID = 7, // Invalid argument. + STATUS_CRC_WRONG = 8, // The CRC_A does not match + STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. + }; + + // A struct used for passing the UID of a PICC. + typedef struct { + byte size; // Number of bytes in the UID. 4, 7 or 10. + byte uidByte[10]; + byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. + } Uid; + + // A struct used for passing a MIFARE Crypto1 key + typedef struct { + byte keyByte[MF_KEY_SIZE]; + } MIFARE_Key; + + // Member variables + Uid uid; // Used by PICC_ReadCardSerial(). + + // Size of the MFRC522 FIFO + static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for setting up the Arduino + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(byte chipAddress); + + ///////////////////////////////////////////////////////////////////////////////////// + // Basic interface functions for communicating with the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(byte reg, byte value); + void PCD_WriteRegister(byte reg, byte count, byte *values); + byte PCD_ReadRegister(byte reg); + void PCD_ReadRegister(byte reg, byte count, byte *values, byte rxAlign = 0); + void setBitMask(unsigned char reg, unsigned char mask); + void PCD_SetRegisterBitMask(byte reg, byte mask); + void PCD_ClearRegisterBitMask(byte reg, byte mask); + byte PCD_CalculateCRC(byte *data, byte length, byte *result); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for manipulating the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_Init(); + void PCD_Reset(); + void PCD_AntennaOn(); + void PCD_AntennaOff(); + byte PCD_GetAntennaGain(); + void PCD_SetAntennaGain(byte mask); + bool PCD_PerformSelfTest(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); + byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); + byte PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); + byte PICC_Select(Uid *uid, byte validBits = 0); + byte PICC_HaltA(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with MIFARE PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + void PCD_StopCrypto1(); + byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); + byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); + byte MIFARE_Decrement(byte blockAddr, long delta); + byte MIFARE_Increment(byte blockAddr, long delta); + byte MIFARE_Restore(byte blockAddr); + byte MIFARE_Transfer(byte blockAddr); + byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + byte MIFARE_GetValue(byte blockAddr, long *value); + byte MIFARE_SetValue(byte blockAddr, long value); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *GetStatusCodeName(byte code); + const __FlashStringHelper *GetStatusCodeName(byte code); + byte PICC_GetType(byte sak); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *PICC_GetTypeName(byte type); + const __FlashStringHelper *PICC_GetTypeName(byte type); + void PICC_DumpToSerial(Uid *uid); + void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); + void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpMifareUltralightToSerial(); + void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + bool MIFARE_OpenUidBackdoor(bool logErrors); + bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); + bool MIFARE_UnbrickUidSector(bool logErrors); + + ///////////////////////////////////////////////////////////////////////////////////// + // Convenience functions - does not add extra functionality + ///////////////////////////////////////////////////////////////////////////////////// + bool PICC_IsNewCardPresent(); + bool PICC_ReadCardSerial(); + +private: + byte _chipAddress; + byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); +}; + +#endif diff --git a/examples/Unit/RFID_RC522/RFID_RC522.ino b/examples/Unit/RFID_RC522/RFID_RC522.ino new file mode 100644 index 0000000..dca5ab6 --- /dev/null +++ b/examples/Unit/RFID_RC522/RFID_RC522.ino @@ -0,0 +1,43 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: RFID. +* date:2021/8/19 +******************************************************************************* + Please connect to Port A,Use the RFID Unit to read the Fudan card ID and display the ID on the screen. + 请连接端口A,使用RFID Unit 读取ID卡并在屏幕上显示。 +*/ + +#include +#include "MFRC522_I2C.h" + +MFRC522 mfrc522(0x28); // Create MFRC522 instance. 创建MFRC522实例 + +void setup() { + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + M5.Lcd.println("MFRC522 Test"); + Wire.begin(); //Wire init, adding the I2C bus. Wire初始化, 加入i2c总线 + + mfrc522.PCD_Init(); // Init MFRC522. 初始化 MFRC522 + M5.Lcd.println("Please put the card\n\nUID:"); +} + +void loop() { + M5.Lcd.setCursor(22,23); + if (!mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial()) { //如果没有读取到新的卡片 + delay(200); + return; + } + M5.Lcd.fillRect(22,23,320,20,BLACK); + for (byte i = 0; i < mfrc522.uid.size; i++) { + M5.Lcd.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); + M5.Lcd.print(mfrc522.uid.uidByte[i], HEX); + } + M5.Lcd.println(""); +} \ No newline at end of file diff --git a/examples/Unit/RGB_SK6812/RGB_SK6812.ino b/examples/Unit/RGB_SK6812/RGB_SK6812.ino new file mode 100644 index 0000000..6d3d5ae --- /dev/null +++ b/examples/Unit/RGB_SK6812/RGB_SK6812.ino @@ -0,0 +1,42 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:RGB. 多彩灯 +* date:2021/8/11 +******************************************************************************* + Please connect to Port ,Control RGB Unit to scroll through three colors of red, green and blue + 请连接端口,控制RGB单元滚动红、绿、蓝三种颜色. +*/ + +#include +#include + +#define PIN 32 //定义NeoPixel的控制引脚 +#define NUMPIXELS 3 //定义NeoPixel控制灯灯数量 + +Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); //set number of LEDs, pin number, LED type. 设置灯的数量,控制引脚编号,灯灯类型 + +void setup() { + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + pixels.begin(); //Init the NeoPixel library. 初始化NeoPixel库 + M5.Lcd.println(("RGB Example")); +} + +int i=0,j=1,k=2; + +void loop() { + pixels.setPixelColor(i++, pixels.Color(100, 0, 0)); // Bright red + pixels.setPixelColor(j++, pixels.Color(0,100,0)); // Bright green + pixels.setPixelColor(k++, pixels.Color(0,0,100)); // Bright blue + pixels.show(); //sends the updated color to the hardware. 将更新后的颜色发送到硬件。 + delay(200); + if(i==3) i=0; + else if(j==3) j=0; + else if(k==3) k=0; +} diff --git a/examples/Unit/RTC_BM8563/RTC_BM8563.ino b/examples/Unit/RTC_BM8563/RTC_BM8563.ino new file mode 100644 index 0000000..d8a999c --- /dev/null +++ b/examples/Unit/RTC_BM8563/RTC_BM8563.ino @@ -0,0 +1,54 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: RTC. 实时时钟 +* date:2021/8/18 +******************************************************************************* + Please connect to Port,The time is displayed on the screen. + 请连接端口,屏幕上显示时间。 +*/ + +#include "M5StickC.h" +#include "M5_BM8563.h" + +BM8563 RTC; + +rtc_time_type RTCtime; +rtc_date_type RTCdate; + +char str_buffer[64]; + +void setup() +{ + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + M5.Lcd.print(" RTC"); + RTC.begin(); //Example Initialize the RTC clock. 初始化RTC时钟 + RTCtime.Hours = 14; //Set the RTC clock time. 设置RTC时钟时间 + RTCtime.Minutes = 40; + RTCtime.Seconds = 5; + + RTCdate.WeekDay = 4; //Set the RTC clock date. 设置RTC时钟日期 + RTCdate.Month = 7; + RTCdate.Date = 15; + RTCdate.Year = 2021; + + RTC.setTime(&RTCtime); //Example Synchronize the set time to the RTC. 将设置的时间同步至RTC + RTC.setDate(&RTCdate); //Synchronize the set date to the RTC. 将设置的日期同步至RTC +} + +void loop() +{ + RTC.getTime(&RTCtime); //To get the time. 获取时间 + RTC.getDate(&RTCdate); //Get the date. 获取日期 + M5.Lcd.setCursor(0,20); + M5.Lcd.printf("RTC Time Now is \n%02d:%02d:%02d\n",RTCtime.Hours, RTCtime.Minutes, RTCtime.Seconds); + M5.Lcd.printf("RTC Date Now is \n%02d:%02d:%02d WeekDay:%02d\n",RTCdate.Year, RTCdate.Month, RTCdate.Date, RTCdate.WeekDay); + delay(1000); + M5.Lcd.fillRect(0,20,320,140,BLACK); +} diff --git a/examples/Unit/Relay/Relay.ino b/examples/Unit/Relay/Relay.ino index d6f2169..22270cb 100644 --- a/examples/Unit/Relay/Relay.ino +++ b/examples/Unit/Relay/Relay.ino @@ -1,17 +1,37 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:Relay. 继电器 +* date:2021/8/16 +******************************************************************************* + Please connect to Port ,Use RELAY to switch on and off the circuit. + 请连接端口,使用继电器开关电路。 +*/ + #include void setup() { - M5.begin(); - //M5.Lcd.clear(BLACK); - M5.Lcd.setTextFont(4); - M5.Lcd.setTextColor(YELLOW, BLACK); - //disable the speak noise + M5.begin(); //Init M5StickC. 初始化 M5StickC + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.setCursor(50, 0); + M5.Lcd.println(("Relay Example")); + dacWrite(25, 0); //disable the speak noise pinMode(32, OUTPUT); } void loop(void) { + M5.Lcd.setCursor(30, 40); + M5.Lcd.print("ON"); digitalWrite(32, HIGH); - delay(500); + delay(1000); + M5.Lcd.fillRect(30,40,60,50,BLACK); + M5.Lcd.print("OFF"); digitalWrite(32, LOW); - delay(500); + delay(1000); + M5.Lcd.fillRect(30,40,60,50,BLACK); } diff --git a/examples/Unit/TOF_VL53L0X/TOF_VL53L0X.ino b/examples/Unit/TOF_VL53L0X/TOF_VL53L0X.ino index 68435e8..c6a184f 100644 --- a/examples/Unit/TOF_VL53L0X/TOF_VL53L0X.ino +++ b/examples/Unit/TOF_VL53L0X/TOF_VL53L0X.ino @@ -1,9 +1,19 @@ -// the original code by Ted Meyers -// posted here: https://groups.google.com/d/msg/diyrovers/lc7NUZYuJOg/ICPrYNJGBgAJ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe:ToF. 激光测距 +* date:2021/8/16 +******************************************************************************* + Please connect to Port,Use ToF Unit to detect distance and display distance data on the screen in real time. + 请连接端口,使用ToF Unit检测距离,并在屏幕上实时显示距离数据。 +*/ -// if your tof have some problem, please see https://docs.m5stack.com/#/en/unit/tof #include -#include #define VL53L0X_REG_IDENTIFICATION_MODEL_ID 0xc0 #define VL53L0X_REG_IDENTIFICATION_REVISION_ID 0xc2 @@ -12,78 +22,10 @@ #define VL53L0X_REG_SYSRANGE_START 0x00 #define VL53L0X_REG_RESULT_INTERRUPT_STATUS 0x13 #define VL53L0X_REG_RESULT_RANGE_STATUS 0x14 -#define address 0x29 +#define address 0x29 //I2C address byte gbuf[16]; -void setup() { - // put your setup code here, to run once: - Wire.begin(); // join i2c bus (address optional for master) - Serial.begin(115200); // start serial for output - Serial.println("VLX53LOX test started."); - - - //---osmar - M5.begin(); - M5.Lcd.fillScreen(BLACK); - M5.Lcd.setCursor(30, 70); - M5.Lcd.setTextColor(WHITE); - M5.Lcd.setTextSize(4); - //---osmar -} - -void loop() { - Serial.println("----- START TEST ----"); - test(); - Serial.println("----- END TEST ----"); - Serial.println(""); - delay(1000); -} - -void test() { - byte val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_REVISION_ID); - Serial.print("Revision ID: "); Serial.println(val1); - - val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_MODEL_ID); - Serial.print("Device ID: "); Serial.println(val1); - - val1 = read_byte_data_at(VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD); - Serial.print("PRE_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1); - Serial.print(" decode: "); Serial.println(VL53L0X_decode_vcsel_period(val1)); - - val1 = read_byte_data_at(VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD); - Serial.print("FINAL_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1); - Serial.print(" decode: "); Serial.println(VL53L0X_decode_vcsel_period(val1)); - - write_byte_data_at(VL53L0X_REG_SYSRANGE_START, 0x01); - - byte val = 0; - int cnt = 0; - while (cnt < 100) { // 1 second waiting time max - delay(10); - val = read_byte_data_at(VL53L0X_REG_RESULT_RANGE_STATUS); - if (val & 0x01) break; - cnt++; - } - if (val & 0x01) Serial.println("ready"); else Serial.println("not ready"); - - read_block_data_at(0x14, 12); - uint16_t acnt = makeuint16(gbuf[7], gbuf[6]); - uint16_t scnt = makeuint16(gbuf[9], gbuf[8]); - uint16_t dist = makeuint16(gbuf[11], gbuf[10]); - byte DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3); - - Serial.print("ambient count: "); Serial.println(acnt); - Serial.print("signal count: "); Serial.println(scnt); - Serial.print("distance "); Serial.println(dist); - Serial.print("status: "); Serial.println(DeviceRangeStatusInternal); - - M5.Lcd.setCursor(0, 70); - M5.Lcd.fillScreen(BLACK); - M5.Lcd.print( dist); - -} - uint16_t bswap(byte b[]) { // Big Endian unsigned short to little endian unsigned short uint16_t val = ((b[0] << 8) & b[1]); @@ -142,7 +84,7 @@ uint16_t read_word_data_at(byte reg) { while (Wire.available() < 2) delay(1); gbuf[0] = Wire.read(); gbuf[1] = Wire.read(); - return bswap(gbuf); + return bswap(gbuf); } void read_block_data_at(byte reg, int sz) { @@ -162,3 +104,38 @@ uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg) { uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) << 1; return vcsel_period_pclks; } + +void setup() { + // put your setup code here, to run once: + Wire.begin(); // join i2c bus (address optional for master) + M5.begin(); + M5.Lcd.setRotation(3); //Rotate the screen. 旋转屏幕 + M5.Lcd.println(("VLX53LOX")); +} + +void loop() { + write_byte_data_at(VL53L0X_REG_SYSRANGE_START, 0x01); + + byte val = 0; + int cnt = 0; + while (cnt < 100) { // 1 second waiting time max + delay(10); + val = read_byte_data_at(VL53L0X_REG_RESULT_RANGE_STATUS); + if (val & 0x01) break; + cnt++; + } + if (val & 0x01) Serial.println("ready"); else Serial.println("not ready"); + + read_block_data_at(0x14, 12); + uint16_t acnt = makeuint16(gbuf[7], gbuf[6]); + uint16_t scnt = makeuint16(gbuf[9], gbuf[8]); + uint16_t dist = makeuint16(gbuf[11], gbuf[10]); + byte DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3); + M5.Lcd.fillRect(0, 20, 319, 239, BLACK); + M5.Lcd.setCursor(0, 20); + M5.Lcd.print("ambient count: "); M5.Lcd.println(acnt); + M5.Lcd.print("signal count: "); M5.Lcd.println(scnt); + M5.Lcd.print("distance: "); M5.Lcd.println(dist); + M5.Lcd.print("status: "); M5.Lcd.println(DeviceRangeStatusInternal); + delay(1000); +} \ No newline at end of file diff --git a/examples/Unit/ULTRA/ULTRA.ino b/examples/Unit/ULTRA/ULTRA.ino index cfd6a06..832e48f 100644 --- a/examples/Unit/ULTRA/ULTRA.ino +++ b/examples/Unit/ULTRA/ULTRA.ino @@ -1,49 +1,48 @@ +/* +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Ultrasonic. 超声波测距传感器 +* date:2021/8/19 +******************************************************************************* + Please connect to Port A,Display the distance measured by ultrasonic + 请连接端口A,显示超声波测量的距离 +*/ + #include void setup() { - // put your setup code here, to run once: - M5.begin(); - - Wire.begin(32,33); - - M5.Lcd.setRotation(3); - M5.Lcd.setCursor(25, 0, 4); - M5.Lcd.print("Ultrasonic"); + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + Wire.begin(32, 33); + M5.Lcd.setCursor(0, 0, 4); //Set the cursor at (0,0) and set the font to a 4 point font. 将光标设置在(0,0)处,且设置字体为4号字体 + M5.Lcd.print("Ultrasonic\nDistance:"); } float readEUS() { - uint32_t data; - Wire.beginTransmission(0x57); - Wire.write(0x01); - Wire.endTransmission(); - delay(120); - Wire.requestFrom(0x57,3); - data = Wire.read();data <<= 8; - data |= Wire.read();data <<= 8; - data |= Wire.read(); - return float(data) / 1000; - + uint32_t data; + Wire.beginTransmission(0x57); //Transfer data to 0x57. 将数据传输到0x57 + Wire.write(0x01); + Wire.endTransmission(); //Stop data transmission with the Ultrasonic Unit. 停止与Ultrasonic Unit的数据传输 + delay(120); + Wire.requestFrom(0x57,3); //Request 3 bytes from Ultrasonic Unit. 向Ultrasonic Unit请求3个字节。 + data = Wire.read();data <<= 8; + data |= Wire.read();data <<= 8; + data |= Wire.read(); + return float(data) / 1000; } void loop() { - - float newvalue = 0; - - while(1) - { - newvalue = readEUS(); - M5.Lcd.setCursor(22,40,4); - - if(( newvalue < 1500 )&&( newvalue > 20 )) - { - M5.Lcd.printf("%.2fmm",newvalue); - if(( newvalue < 100 )&&( newvalue > 20 )) - { - M5.Lcd.fillRect(128, 40, 15, 20, BLACK); - } - } - delay(100); - } - + static float newvalue = 0; + newvalue = readEUS(); + if(( newvalue < 1500 )&&( newvalue > 20 )){ + M5.Lcd.setCursor(0,27); + M5.Lcd.printf("%.2fmm",newvalue); + } + delay(100); } diff --git a/examples/Unit/VIBRATOR/VIBRATOR.ino b/examples/Unit/VIBRATOR/VIBRATOR.ino index 95e0551..761d7bc 100644 --- a/examples/Unit/VIBRATOR/VIBRATOR.ino +++ b/examples/Unit/VIBRATOR/VIBRATOR.ino @@ -1,26 +1,37 @@ /* - Description: Adjust the speed of VIBRATOR Unit through PWM. +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Vibrator. 震动电机 +* date:2021/8/19 +******************************************************************************* + Please connect to Port,Adjust the speed of VIBRATOR Unit through PWM. + 请连接端口,通过PWM调节Vibrator Unit的速度。 */ + #include -const int motor_pin = 32; +#define motor_pin 32 int freq = 10000; int ledChannel = 0; int resolution = 10; void setup() { - // put your setup code here, to run once: - M5.begin(); - M5.Lcd.setCursor(20, 10, 2); - M5.Lcd.println("MOTOR"); - ledcSetup(ledChannel, freq, resolution); - ledcAttachPin(motor_pin, ledChannel); - + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.lcd.setTextSize(2); //Set the text size to 2. 设置文字大小为2 + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + M5.Lcd.setCursor(40, 10); //Set the cursor at (40,10). 将光标设置在(40,10)处 + M5.Lcd.println("Vibrator"); + ledcSetup(ledChannel, freq, resolution); //Sets the frequency and number of counts corresponding to the channel. 设置通道对应的频率和计数位数 + ledcAttachPin(motor_pin, ledChannel); //Binds the specified channel to the specified I/O port for output. 将指定通道绑定到指定 IO 口上以实现输出 } void loop() { - // put your main code here, to run repeatedly: - ledcWrite(ledChannel, 512); - delay(1000); - ledcWrite(ledChannel, 0); - delay(1000); + ledcWrite(ledChannel, 512); //Output PWM. 输出PWM + delay(1000); + ledcWrite(ledChannel, 0); + delay(1000); } diff --git a/examples/Unit/WEIGHT/WEIGHT.ino b/examples/Unit/WEIGHT/WEIGHT.ino index db0a948..2a7112d 100644 --- a/examples/Unit/WEIGHT/WEIGHT.ino +++ b/examples/Unit/WEIGHT/WEIGHT.ino @@ -1,53 +1,52 @@ /* - This Unit connects to GRVOE B on M5Core. - hx711 library fork from https://github.com/aguegu/ardulibs/tree/master/hx711 +******************************************************************************* +* Copyright (c) 2021 by M5Stack +* Equipped with M5Stick-C sample source code +* 配套 M5Stick-C 示例源代码 +* Visit the website for more information:https://docs.m5stack.com/en/core/m5stickc +* 获取更多资料请访问:https://docs.m5stack.com/zh_CN/core/m5stickc +* +* describe: Weight. +* date:2021/8/20 +******************************************************************************* + Please connect to Port,Use WEIGHT Unit to read the analog value returned by the pressure sensor, + convert it into intuitive weight data and send it to M5Core,Press ButtonA to calibrate + 请连接端口,使用WEIGHT Unit读取压力传感器返回的模拟值,将其转换为直观的重量数据,发送到M5Core,按下按钮A进行校准 */ -#include -#include "hx711.h" +#include +#include "HX711.h" -#define FRONT 1 - -#define X_LOCAL 40 -#define Y_LOCAL 40 -#define X_F 30 -#define Y_F 30 - -HX711 scale(33, 32); - -void header(const char *string, uint16_t color){ - M5.Lcd.fillScreen(color); - M5.Lcd.setTextSize(1); - M5.Lcd.setTextColor(TFT_MAGENTA, TFT_BLUE); - M5.Lcd.fillRect(0, 0, 320, 30, TFT_BLUE); - M5.Lcd.setTextDatum(TC_DATUM); - M5.Lcd.drawString(string, 160, 3, 4); -} +// HX711 related pin Settings. HX711 相关引脚设置 +#define LOADCELL_DOUT_PIN 33 +#define LOADCELL_SCK_PIN 32 +HX711 scale; +const long LOADCELL_OFFSET = 50682624; +const long LOADCELL_DIVIDER = 5895655; void setup() { - M5.begin(); - M5.Lcd.setRotation(3); - M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); - M5.Lcd.setCursor(2, 2,FRONT); - M5.Lcd.print("Connect Unit to GROVE B"); - - M5.Lcd.setCursor(2,11,FRONT); - M5.Lcd.print("Btn_A to reset"); - Serial.begin(115200); + M5.begin(); //Init M5StickC. 初始化M5StickC + M5.Lcd.setRotation(3); //Rotating display. 旋转显示屏 + M5.Lcd.setCursor(0, 0); //Set the cursor at (0,0). 将光标设置在(0,0)处 + M5.Lcd.print("Weight Unit\nBtnA to reset"); + scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); // Initialize library with data output pin, clock input pin and gain factor. 初始化库的数据输出引脚,时钟输入引脚和增益因子。 + scale.set_scale(LOADCELL_DIVIDER); //set the SCALE value this value is used to convert the raw data to measure units. 设置SCALE值,该值用于将原始数据转换为度量单位 + scale.set_offset(LOADCELL_OFFSET); //Set the tare weight. 设置皮重 + scale.set_scale(61.2f); // this value is obtained by calibrating the scale with known weights; see the README for details. 这个值是通过校正已知权重的刻度而得到的 + scale.tare(); // reset the scale to 0. 将比例重置为0 } -float weight; - -void loop(){ - M5.update(); - if (M5.BtnA.wasReleased()) { - scale.setOffset(scale.averageValue()); - } - weight =scale.getGram(); - Serial.println(weight); - M5.Lcd.setCursor(2,21,FRONT); - M5.Lcd.printf(" "); - M5.Lcd.setCursor(2,31,FRONT); - M5.Lcd.printf("weight:%0.2f g ",weight); -} +void loop() { + M5.update(); + if (M5.BtnA.wasPressed()) { //If button A is pressed. 如果按键A按下 + scale.set_offset(LOADCELL_OFFSET + scale.read()); + scale.set_scale(61.2f); + scale.tare(); + } + static int weight = scale.get_units(5); + M5.Lcd.fillRect(75, 40, 80, 30, BLACK); + M5.Lcd.setCursor(30,40); + M5.Lcd.printf("Weight:%1d g", weight); + delay(200); +} \ No newline at end of file diff --git a/examples/Unit/WEIGHT/hx711.cpp b/examples/Unit/WEIGHT/hx711.cpp deleted file mode 100644 index 8637c32..0000000 --- a/examples/Unit/WEIGHT/hx711.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * hx711.cpp - * - * Created on: Oct 31, 2012 - * Author: agu - */ - -#include "hx711.h" - -HX711::HX711(uint8_t pin_dout, uint8_t pin_slk) : - _pin_dout(pin_dout), _pin_slk(pin_slk) -{ - pinMode(_pin_slk, OUTPUT); - pinMode(_pin_dout, INPUT); - - digitalWrite(_pin_slk, HIGH); - delayMicroseconds(100); - digitalWrite(_pin_slk, LOW); - - averageValue(); - this->setOffset(averageValue()); - this->setScale(); -} - -HX711::~HX711() -{ - -} - -long HX711::averageValue(byte times) -{ - long sum = 0; - for (byte i = 0; i < times; i++) - { - sum += getValue(); - } - - return sum / times; -} - -long HX711::getValue() -{ - byte data[3]; - - while (digitalRead(_pin_dout)) - ; - - for (byte j = 0; j < 3; j++) - { - for (byte i = 0; i < 8; i++) - { - digitalWrite(_pin_slk, HIGH); - bitWrite(data[2 - j], 7 - i, digitalRead(_pin_dout)); - digitalWrite(_pin_slk, LOW); - } - } - - digitalWrite(_pin_slk, HIGH); - digitalWrite(_pin_slk, LOW); - - return ((long) data[2] << 16) | ((long) data[1] << 8) | (long) data[0]; -} - -void HX711::setOffset(long offset) -{ - _offset = offset; -} - -void HX711::setScale(float scale) -{ - _scale = scale; -} - -float HX711::getGram() -{ - long val = (averageValue() - _offset); - return (float) val / _scale; -} \ No newline at end of file diff --git a/examples/Unit/WEIGHT/hx711.h b/examples/Unit/WEIGHT/hx711.h deleted file mode 100644 index 9000dc2..0000000 --- a/examples/Unit/WEIGHT/hx711.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HX711_H_ -#define HX711_H_ - -#include "Arduino.h" - -class HX711 -{ -public: - HX711(uint8_t pin_din, uint8_t pin_slk); - virtual ~HX711(); - long getValue(); - long averageValue(byte times = 3); - void setOffset(long offset); - void setScale(float scale = 742.f); - float getGram(); - -private: - const uint8_t _pin_dout; - const uint8_t _pin_slk; - long _offset; - float _scale; -}; - -#endif /* HX711_H_ */ \ No newline at end of file diff --git a/library.json b/library.json index fe02e1c..a3ad558 100644 --- a/library.json +++ b/library.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/m5stack/M5StickC.git" }, - "version": "0.2.1", + "version": "0.2.3", "framework": "arduino", "platforms": "espressif32" } diff --git a/library.properties b/library.properties index fcda4fd..1117562 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5StickC -version=0.2.1 +version=0.2.3 author=M5StickC maintainer=Yang Zhou sentence=Library for M5StickC Core development kit @@ -8,3 +8,4 @@ category=Device Control url=https://github.com/m5stack/M5StickC.git architectures=esp32 includes=M5StickC.h +depends=UNIT_ENV,Adafruit MCP4725,Adafruit TCS34725,Adafruit NeoPixel,MAX30100lib,FastLED,MFRC522_I2C,M5GFX,M5_BM8563,M5_ADS1100,M5_ADS1115,HX711 Arduino Library,PCA9554