From 9d4a2394661596924f2536e91e6ce6b3f39eca06 Mon Sep 17 00:00:00 2001 From: Jaro Date: Fri, 22 Jan 2021 06:04:56 +0100 Subject: [PATCH] Init --- .gitignore | 5 + .vscode/extensions.json | 7 ++ include/README | 39 +++++++ lib/README | 46 ++++++++ platformio.ini | 16 +++ src/XModem.cpp | 251 ++++++++++++++++++++++++++++++++++++++++ src/XModem.h | 57 +++++++++ src/main.cpp | 205 ++++++++++++++++++++++++++++++++ test/README | 11 ++ 9 files changed, 637 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/XModem.cpp create mode 100644 src/XModem.h create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e80666b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..c1c994f --- /dev/null +++ b/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32cam] +platform = espressif32 +board = esp32cam +framework = arduino +monitor_port = COM[13678] +monitor_speed = 115200 diff --git a/src/XModem.cpp b/src/XModem.cpp new file mode 100644 index 0000000..b859d20 --- /dev/null +++ b/src/XModem.cpp @@ -0,0 +1,251 @@ +/* + XModem.cpp + XModem/YModem Tx library + (C) Mattheij Computer Service 1994 + Modified by Tomonobu.Saito@gmail.com 2019 +*/ + +#include "XModem.h" + +// Number of seconds until giving up hope of receiving sync packets from +// host. +#define SYNC_TIMEOUT 30 +// Number of times we try to send a packet to the host until we give up +// sending.. +#define MAX_RETRY 30 + +/* Initialize XModem session */ +XModem::XModem(Stream *port, char mode) : + port(port), packetNo(1), checksumBuf(0), filepos(0), packetLen(128), crcBuf(0), mode(mode) +{ +} + +/* Send out a byte of payload data, + includes checksumming +*/ +void XModem::outputByte(unsigned char inChar) +{ + char j; + checksumBuf += inChar; + + crcBuf = crcBuf ^ (int) inChar << 8; + j = 8; + do + { + if (crcBuf & 0x8000) + crcBuf = crcBuf << 1 ^ 0x1021; + else + crcBuf = crcBuf << 1; + } while (--j); + + port->write(inChar); +} + +/* Wait for either C or NACK as a sync packet. + Determines protocol details, like block size + and checksum algorithm. +*/ +char XModem::sync(void) +{ + char tryNo; + char inChar; + // Wait a second every time until timeout. + // The magic char expected from the host is 'C' or + // NAK + port->setTimeout(1000); + tryNo = 0; + do + { + port->readBytes(&inChar, 1); + tryNo++; + // When timed out, leave immediately + if (tryNo == SYNC_TIMEOUT) + return (-1); + } while ((inChar != 'C') && (inChar != NAK)); + + // Determine which checksum algorithm to use + // this routine also determines the packet length + this->packetLen = 128; + this->oldChecksum = (inChar == NAK) ? 1 : 0; + + return (0); +} + +/** Wait for the remote to acknowledge or cancel. + Returns the received char if no timeout occured or + a CAN was received. In this cases, it returns -1. + **/ +char XModem::waitACK(void) +{ + char i, inChar; + i = 0; + do + { + port->readBytes(&inChar, 1); + i++; + if (i > 200) + return (-1); + if (inChar == CAN) + return CAN; + } while ((inChar != NAK) && (inChar != ACK) && (inChar != 'C')); + return (inChar); +} + +void XModem::sendFile(uint8_t *dataFile,size_t len, char *fileName) +{ + char inChar; + int i; + int pos; + unsigned char tryNo; + + // Rewind data file before sending the file.. + pos = 0; + + // When doing YModem, send block 0 to inform host about + // file name to be received + if (this->mode == ModeYModem) + { + if (this->sync() != 0) + goto err; + + // Send header for virtual block 0 (file name) + port->write(SOH); + port->write((uint8_t)0); + port->write(0xFF); + + for (i = 0; i < strlen(fileName); i++) + { + this->outputByte(fileName[i]); + } + char filesize[16]; + memset(filesize, 0, sizeof(filesize)); + sprintf(filesize, "%d", len); + this->outputByte((uint8_t)0x00); + ++i; + for (int j = 0; j < strlen(filesize); ++j, ++i) { + this->outputByte(filesize[j]); + } + for (; i < 128; i++) + { + this->outputByte((uint8_t)0x00); + } + if (oldChecksum) + port->write((char)checksumBuf); + else + { + port->write((char)(crcBuf >> 8)); + port->write((char)(crcBuf & 0xFF)); + } + // Discard ACK/NAK/CAN, in case + // we communicate to an XMODEM-1k client + // which might not know about the 0 block. + waitACK(); + } + + if (this->sync() != 0) + goto err; + + packetNo = 1; + while (pos < len) + { + filepos = pos; + + // Sending a packet will be retried + tryNo = 0; + do + { + // Seek to start of current data block, + // will advance through the file as + // block will be acked.. + pos = filepos; + + // Reset checksum stuff + checksumBuf = 0x00; + crcBuf = 0x00; + + // Try to use 1K(1024 byte) mode if possible + if (ModeYModem == mode && 128 < len-pos) { + packetLen = 1024; // 1K mode + } else { + packetLen = 128; // normal mode + } + + // Try to send packet, so header first + if (packetLen == 128) + port->write(SOH); + else + port->write(STX); + + port->write(packetNo); + port->write(~packetNo); + for (i = 0; i < packetLen; i++) + { + inChar = (0 < len-pos) ? dataFile[pos++] : XEOF; + this->outputByte(inChar); + } + // Send out checksum, either CRC-16 CCITT or + // classical inverse of sum of bytes. + // Depending on how the received introduced himself + if (oldChecksum) + port->write((char)checksumBuf); + else + { + port->write((char)(crcBuf >> 8)); + port->write((char)(crcBuf & 0xFF)); + } + + inChar = waitACK(); + if (CAN == inChar) goto err; + tryNo++; + if (tryNo > MAX_RETRY) + goto err; + } while (inChar != ACK); + + packetNo++; + } + // Send EOT and wait for ACK + tryNo = 0; + do + { + port->write(EOT); + inChar = waitACK(); + tryNo++; + // When timed out, leave immediately + if (tryNo == SYNC_TIMEOUT) + goto err; + } while (inChar != ACK); + + // Send "all 00 data" to finish YModem. + if (this->mode == ModeYModem) { + // wait 'C' from Rx(PC) + this->sync(); + // send header. + port->write(SOH); + port->write((uint8_t)0x00); + port->write((uint8_t)0xFF); + // Reset checksum stuff + checksumBuf = 0x00; + crcBuf = 0x00; + // send all '00' data (128byte) + for (i = 0; i < 128; ++i) { + this->outputByte(0x00); + } + // send checksum/CRC + if (oldChecksum) { + port->write((char)checksumBuf); // need debug + } else { + port->write((uint8_t)(crcBuf >> 8)); + port->write((uint8_t)(crcBuf & 0xFF)); + } + // Wait ACK from Rx. + if (ACK != waitACK()) + goto err; + } + + port->println("Finish sending."); + return; + + // When we get here everything was successful. +err: + port->println("Error sending..."); +} diff --git a/src/XModem.h b/src/XModem.h new file mode 100644 index 0000000..6c2e061 --- /dev/null +++ b/src/XModem.h @@ -0,0 +1,57 @@ +/* + XModem.h + XModem/YModem constants + (C) Mattheij Computer Service 1994 + Modified by Tomonobu.Saito@gmail.com 2019 +*/ + +/* $Id: XModem.h,v 1.53 2012/10/24 19:03:14 deuce Exp $ */ + +#ifndef _XMODEM_H +#define _XMODEM_H + +#include + +/* + ascii constants +*/ + +#ifndef SOH +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ENQ 0x05 +#define ACK 0x06 +#define LF 0x0a +#define CR 0x0d +#define DLE 0x10 +#define XON 0x11 +#define XOFF 0x13 +#define NAK 0x15 +#define CAN 0x18 +#define XEOF 0x1a +#endif + +#define ModeXModem 0 +#define ModeYModem 1 + +class XModem +{ + public: + XModem(Stream *port, char mode); + void sendFile(uint8_t *dataFile,size_t len,char *fileName); + private: + Stream *port; + unsigned char packetNo, checksumBuf; + long filepos; + unsigned int packetLen; + int crcBuf; + unsigned char mode; + unsigned char oldChecksum; + + char sync(void); + void outputByte(unsigned char inChar); + char waitACK(void); +}; + +#endif // _XMODEM_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d5c034a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,205 @@ +#include +#include +#include "esp_camera.h" +#include "Arduino.h" +#include "soc/soc.h" // Disable brownour problems +#include "soc/rtc_cntl_reg.h" // Disable brownour problems +#include "driver/rtc_io.h" +#include // read and write from flash memory + +// define the number of bytes you want to access +#define EEPROM_SIZE 1 + +// Pin definition for CAMERA_MODEL_AI_THINKER +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +int pictureNumber = 0; +camera_fb_t * fb = NULL; +XModem xmodem(&Serial, ModeYModem); + +typedef void (*action_func_t)(char *aLine); + +typedef struct { + char command[7+1]; // max 7 characters plus '\0' terminator + action_func_t action; +} command_action_t; + + +void toLower(char *s) { + while (*s) { + if (isupper(*s)) { + *s += 0x20; + } + s++; + } +} + +void print_commands(char *aLine); + +void reboot_esp32(char *aLine) +{ + Serial.println("[OK] GOING TO REBOOT"); + delay(1000); + ESP.restart(); +} + +void recv_ymodem(char *aLine) +{ + xmodem.sendFile(fb->buf, fb->len, "camera.jpg"); +} + +void capture_image(char *aLine) +{ + fb = esp_camera_fb_get(); + Serial.println("[OK] CAPTURED IMAGE"); +} + +void free_image(char *aLine) +{ + esp_camera_fb_return(fb); + Serial.println("[OK] FREE CAPTURED IMAGE"); +} + +void print_measurement(char *aLine) +{ + Serial.printf("temperature=%.2f humidity=%.2f pressure=%.2f\n",temperatureRead(),60.2,903.45); + return; +} + +const command_action_t commands[] = { + // Name of command user types, function that implements the command. + {"reboot", reboot_esp32}, + {"capture", capture_image}, + {"free", free_image}, + {"measure", print_measurement}, + {"rb", recv_ymodem}, + {"help", print_commands}, + {"?", print_commands}, +}; + +void print_commands(char *aLine) { + Serial.print(commands[0].command); + for (size_t i = 1; i < sizeof(commands)/sizeof(commands[0]); i++) { + Serial.print(','); Serial.print(commands[i].command); + } + Serial.println(); +} + + +void execute(char *aLine) { + if (aLine == NULL || *aLine == '\0') return; + char *cmd = strtok(aLine, " \t"); + if (cmd == NULL || *cmd == '\0') return; + toLower(cmd); + for (size_t i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + if (strcmp(cmd, commands[i].command) == 0) { + commands[i].action(aLine); + return; + } + } + Serial.println("command not found"); +} + + +void setup() { + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector + + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for Leonardo only + } + +camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + + if(psramFound()){ + config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA + config.jpeg_quality = 10; + config.fb_count = 2; + } else { + config.frame_size = FRAMESIZE_SVGA; + config.jpeg_quality = 12; + config.fb_count = 1; + } + +// Init Camera + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + Serial.printf("Camera init failed with error 0x%x", err); + return; + } + + // Take Picture with Camera + fb = esp_camera_fb_get(); + if(!fb) { + Serial.println("Camera capture failed"); + return; + } + esp_camera_fb_return(fb); + +} + +uint8_t bytesIn; +char aLine[80+1]; + + +void loop() { + if (Serial.available() > 0) { + int b = Serial.read(); + if (b != -1) { + switch (b) { + case '\n': + case '\r': + if (b == '\n' && bytesIn && aLine[bytesIn-1] == '\0') break; + Serial.println(); + aLine[bytesIn] = '\0'; + execute(aLine); + bytesIn = 0; + break; + default: + aLine[bytesIn++] = (char)b; + if (bytesIn >= sizeof(aLine)-1) { + aLine[bytesIn] = '\0'; + execute(aLine); + bytesIn = 0; + } + break; + } + } + } +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..b94d089 --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html