Skip to content

Instantly share code, notes, and snippets.

@mhanuel26
Created March 5, 2019 04:57
Show Gist options
  • Save mhanuel26/a9f0a8f9a2e194a06358085291ed0ed2 to your computer and use it in GitHub Desktop.
Save mhanuel26/a9f0a8f9a2e194a06358085291ed0ed2 to your computer and use it in GitHub Desktop.
//
// 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