-
-
Save mhanuel26/a9f0a8f9a2e194a06358085291ed0ed2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WebCam project based on Spresense Hardware by Manuel Iglesias, Feb. 2019 | |
// | |
#include <SPI.h> | |
#include <Ethernet.h> | |
#include <Flash.h> | |
#include <SDHCI.h> | |
#include <TinyWebServer.h> | |
#include <WebSockets.h> | |
#include <WebSocketsClient.h> | |
#include <WebSocketsServer.h> | |
#include <Camera.h> | |
#define WIZSSIZE 4096 // 4 sockets | |
//#define WIZSSIZE 16384 | |
#define PIN_SPI_SS_ETHERNET_LIB 8 | |
// Enter a MAC address and IP address for your controller below. | |
// The IP address will be dependent on your local network: | |
byte mac[] = { | |
0x00, 0x08, 0xDC, 0x1D, 0x2C, 0x3D | |
}; | |
IPAddress ip(192, 168, 1, 145); | |
SDClass SD; | |
typedef struct { | |
const char* wb_name; | |
CAM_WHITE_BALANCE cam_wb; | |
} camera_wb_list; | |
camera_wb_list whitebalance_list[]{ | |
{"CAM_WHITE_BALANCE_INCANDESCENT", CAM_WHITE_BALANCE_INCANDESCENT}, | |
{"CAM_WHITE_BALANCE_FLUORESCENT", CAM_WHITE_BALANCE_FLUORESCENT}, | |
{"CAM_WHITE_BALANCE_DAYLIGHT", CAM_WHITE_BALANCE_DAYLIGHT}, | |
{"CAM_WHITE_BALANCE_FLASH", CAM_WHITE_BALANCE_FLASH}, | |
{"CAM_WHITE_BALANCE_CLOUDY", CAM_WHITE_BALANCE_CLOUDY}, | |
{"CAM_WHITE_BALANCE_SHADE", CAM_WHITE_BALANCE_SHADE}, | |
}; | |
CAM_WHITE_BALANCE camera_wb = CAM_WHITE_BALANCE_DAYLIGHT; | |
bool cam_cfg_change = false; | |
volatile bool camStreamRdy = false; | |
uint8_t * imageBuf = NULL; | |
size_t imageSize = 0; | |
int imageHeight = 0; | |
int imageWidth = 0; | |
unsigned char start_flag = 0xAA; | |
unsigned char end_flag = 0xFF; | |
unsigned char ip_flag = 0x11; | |
IPAddress localip; | |
#define WEBSOCK_PORT 9001 | |
WebSocketsServer webSocket(WEBSOCK_PORT); // create a websocket server on port 81 | |
void startWebSocket() { // Start a WebSocket server | |
webSocket.begin(); // start the websocket server | |
webSocket.onEvent(webSocketEvent); // if there's an incomming websocket message, go to function 'webSocketEvent' | |
Serial.printf("WebSocket server started at port %d\n\r", WEBSOCK_PORT); | |
} | |
boolean file_handler(TinyWebServer& web_server); | |
boolean index_handler(TinyWebServer& web_server); | |
boolean white_balance_handler(TinyWebServer& web_server, char* buffer, int size); | |
boolean has_filesystem = true; | |
File file; | |
TinyWebServer::PathHandler handlers[] = { | |
{"/variable" "*", TinyWebServer::GET, &TinyWebFormHandler::form_handler }, | |
{"/submitform" "*", TinyWebServer::POST, &TinyWebFormHandler::form_handler }, | |
{"/", TinyWebServer::GET, &index_handler }, | |
{"/" "*", TinyWebServer::GET, &file_handler }, | |
{NULL}, | |
}; | |
TinyWebFormHandler::FormList form_handler_list[] = { | |
{"available", NULL}, | |
{"whitebalance", &white_balance_handler}, | |
{"camera_value_get", &camera_value_get}, | |
{NULL}, | |
}; | |
// FORM INIT | |
// somehow this is hardwired | |
void init_form_keywords(void){ | |
TinyWebFormHandler::add_form_keyword("submitform"); | |
TinyWebFormHandler::add_form_keyword("variable"); | |
TinyWebFormHandler::list_keywords(); | |
} | |
// ********************* | |
// FORM HANDLERS | |
//********************** | |
// GET | |
boolean camera_value_get(TinyWebServer& web_server, char* buffer, int size) { | |
// Serial.println("GET camera_value_get"); | |
// Serial.println(web_server.get_path()); | |
// Send values ... | |
web_server.send_error_code(200); | |
web_server.send_content_type("text/plain"); | |
web_server.end_headers(); | |
String variables = "whitebalance="; | |
for(int i=0; i<sizeof(whitebalance_list); i++){ | |
if(camera_wb == whitebalance_list[i].cam_wb){ | |
variables += whitebalance_list[i].wb_name; | |
break; | |
} | |
} | |
web_server << F(variables.c_str()); | |
return true; | |
} | |
// POST | |
boolean white_balance_handler(TinyWebServer& web_server, char* buffer, int size){ | |
// Serial.println("FORM white_balance_handler"); | |
// Serial.println(web_server.get_path()); | |
char *whitebalance = TinyWebFormHandler::get_var_from_post_data(buffer, "whitebalance"); | |
// adjust white balance variable camera_wb | |
for(int i=0; i<sizeof(whitebalance_list); i++){ | |
if(!strcmp(whitebalance_list[i].wb_name, whitebalance)){ | |
camera_wb = whitebalance_list[i].cam_wb; | |
cam_cfg_change = true; | |
break; | |
} | |
} | |
free(whitebalance); | |
web_server.send_error_code(200); | |
web_server.end_headers(); | |
return true; | |
} | |
// ********************* | |
// END FORM HANDLERS | |
//********************** | |
boolean file_handler(TinyWebServer& web_server) { | |
if(!has_filesystem) { | |
web_server.send_error_code(500); | |
web_server << F("Internal Server Error"); | |
return true; | |
} | |
char* filename = TinyWebServer::get_file_from_path(web_server.get_path()); | |
if(!filename) { | |
web_server.send_error_code(400); | |
web_server << F("Bad Request"); | |
return true; | |
} | |
send_file_name(web_server, filename); | |
free(filename); | |
return true; | |
} | |
void send_file_name(TinyWebServer& web_server, const char* filename) { | |
TinyWebServer::MimeType mime_type | |
= TinyWebServer::get_mime_type_from_filename(filename); | |
file = SD.open(filename, FILE_READ); | |
if (file) { | |
web_server.send_error_code(200); | |
web_server.send_content_type(mime_type); | |
web_server.end_headers(); | |
Serial << F("Read file "); Serial.println(filename); | |
web_server.send_file(file); | |
file.close(); | |
} else { | |
web_server.send_error_code(404); | |
web_server.send_content_type("text/plain"); | |
web_server.end_headers(); | |
Serial << F("Could not find file: "); Serial.println(filename); | |
web_server << F("404 - File not found") << filename << "\n"; | |
} | |
} | |
boolean index_handler(TinyWebServer& web_server) { | |
web_server.send_error_code(200); | |
web_server.end_headers(); | |
web_server << F("<html><body><h1>Hello World Spresense SD!</h1></body></html>\n"); | |
return true; | |
} | |
const char* headers[] = { | |
"Content-Length", | |
NULL | |
}; | |
boolean has_ip_address = false; | |
TinyWebServer web = TinyWebServer(handlers, headers); | |
const char* ip_to_str(const uint8_t* ipAddr) | |
{ | |
static char buf[16]; | |
sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]); | |
return buf; | |
} | |
void listSDFile() { | |
Serial.println("Size\tFilename"); | |
Serial.println("----\t--------"); | |
File root = SD.open("/"); | |
listDirectory(root); | |
} | |
void listDirectory(File dir) { | |
while (true) { | |
File entry = dir.openNextFile(); | |
if (!entry) { | |
break; | |
} | |
if (entry.isDirectory()) { | |
listDirectory(entry); // recursive call | |
} else { | |
Serial.print(entry.size(), DEC); | |
Serial.print("\t"); | |
Serial.println(entry.name()); | |
} | |
entry.close(); | |
} | |
} | |
void webSocketEvent(uint8_t num, WStype_t typein, uint8_t * payload, size_t payloadlength) { // When a WebSocket message is received | |
int blk_count = 0; | |
char canvas_Q_VGA[] = "canvas-Q-VGA"; | |
char ipaddr[26]; | |
switch (typein) { | |
case WStype_DISCONNECTED: // if the websocket is disconnected | |
Serial.printf("[%d]", num); | |
Serial.print(" Disconnected!\n"); | |
break; | |
case WStype_CONNECTED: { // if a new websocket connection is established | |
delay(1000); | |
webSocket.sendBIN(num, &ip_flag, 1); | |
// Serial.printf("WS Connected [%d]", num); | |
IPAddress ip = webSocket.remoteIP(num); | |
Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); | |
// parse local IP address | |
sprintf(ipaddr, "%d.%d.%d.%d", localip[0], localip[1], localip[2], localip[3]); | |
Serial.printf("Local IP of WS: %s\n\r", ipaddr); | |
webSocket.sendTXT(num, (const char *)ipaddr, strlen(ipaddr)); | |
} | |
break; | |
case WStype_TEXT: // if new text data is received | |
if (payloadlength == sizeof(canvas_Q_VGA)-1) { | |
if (memcmp(canvas_Q_VGA, payload, payloadlength) == 0) { | |
webSocket.sendBIN(num, &end_flag, 1); | |
} | |
} | |
if(cam_cfg_change == true){ // if configuration need to change | |
cam_cfg_change = false; | |
// change camera configuration | |
Serial.print("Adjusting new white balance value"); | |
theCamera.setAutoWhiteBalanceMode(camera_wb); | |
delay(1); | |
} | |
// capture image from camera stream | |
theCamera.startStreaming(true, CamCB); | |
for(int j=0; j < 1000; j++){ | |
delay(1); | |
if(camStreamRdy == true){ | |
break; | |
} | |
} | |
if (camStreamRdy == true) | |
{ | |
camStreamRdy = false; | |
theCamera.startStreaming(false, CamCB); | |
if(imageBuf != NULL){ | |
size_t len = imageSize/2; | |
blk_count = 2; | |
int j = 0; | |
for (int i=0; i<blk_count; i++) { | |
if (i == 0) { | |
webSocket.sendBIN(num, &start_flag, 1); | |
} | |
if (i == blk_count-1) { | |
webSocket.sendBIN(num, &end_flag, 1); | |
} | |
webSocket.sendBIN(num, &imageBuf[j], len); | |
j += len; | |
} | |
} //imageBuf != NULL | |
} | |
break; | |
case WStype_ERROR: // if new text data is received | |
Serial.println("Error"); | |
default: | |
Serial.print("WStype "); | |
Serial.print(typein); | |
Serial.println(" not handled \n"); | |
} | |
} | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("Setting up SD card..."); | |
if (!SD.begin()) { | |
has_filesystem = false; | |
Serial.println("SD card is not present"); | |
} | |
Serial.println("Enter any character to Start USB MSC"); | |
bool startMSC = false; | |
int starttime = millis(); | |
while (!Serial.available()){ | |
if((millis() - starttime) > 3000){ | |
break; | |
} | |
} | |
if(Serial.available()){ | |
while (Serial.available()) { | |
Serial.read(); // dummy read to empty input buffer | |
} | |
startMSC = true; | |
} | |
if(startMSC){ | |
Serial.println("Starting USB MSC"); | |
// Start USB Mass Storage | |
if (SD.beginUsbMsc()) { | |
Serial.println("UsbMsc connect error"); | |
} | |
Serial.println("Finish USB MSC? (y/n)"); | |
while (true) { | |
while (!Serial.available()); | |
if(Serial.read() == 'y'){ | |
while (Serial.available()) { | |
Serial.read(); // dummy read to empty input buffer | |
} | |
break; | |
} | |
} | |
// Finish USB Mass Storage | |
if (SD.endUsbMsc()) { | |
Serial.println("UsbMsc disconnect error"); | |
} | |
Serial.println("<<< Finish USB Mass Storage Operation"); | |
delay(100); | |
} | |
// Display SD Card File List | |
listSDFile(); | |
init_form_keywords(); | |
TinyWebFormHandler::set_form_handler(form_handler_list); | |
/* begin() without parameters means that | |
* number of buffers = 1, 30FPS, QVGA, YUV 4:2:2 format */ | |
Serial.println("WebSockets Camera Demo"); | |
Serial.println("Prepare camera"); | |
theCamera.begin(1, | |
CAM_VIDEO_FPS_30, | |
CAM_IMGSIZE_QVGA_H, | |
CAM_IMGSIZE_QVGA_V, | |
CAM_IMAGE_PIX_FMT_YUV422); | |
/* Start video stream. | |
* If received video stream data from camera device, | |
* camera library call CamCB. | |
*/ | |
Serial.println("Streaming starts with New Websocket"); | |
theCamera.startStreaming(false, CamCB); | |
/* Auto white balance configuration */ | |
Serial.println("Set Auto white balance Default parameter"); | |
theCamera.setAutoWhiteBalanceMode(camera_wb); | |
/* Set parameters about still picture. | |
* In the following case, QUADVGA and JPEG. | |
*/ | |
Serial.printf("Setting up the Wiz550io Ethernet Module\n"); | |
Ethernet.init(8); | |
Ethernet.begin(mac); | |
// Ethernet.begin(mac, ip); | |
// Start the web server. | |
Serial.println("Web server starting..."); | |
web.begin(); | |
localip = Ethernet.localIP(); | |
Serial.print("Ready to accept HTTP requests at "); | |
Serial.println(localip); | |
startWebSocket(); | |
} | |
/** | |
* Callback from Camera library when video frame is captured. | |
*/ | |
void CamCB(CamImage img) | |
{ | |
/* Check the img instance is available or not. */ | |
if (img.isAvailable()) | |
{ | |
/* convert image data format to RGB565 */ | |
img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565); | |
// Set some image variables needed for Streaming process | |
imageBuf = img.getImgBuff(); | |
imageSize = img.getImgSize(); | |
imageHeight = img.getHeight(); | |
imageWidth = img.getWidth(); | |
// Signal a new Frame is Ready | |
camStreamRdy = true; | |
} | |
else | |
{ | |
Serial.print("Failed to get video stream image\n"); | |
} | |
} | |
void loop() { | |
webSocket.loop(); | |
web.process(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment