mirror of https://github.com/jeelabs/esp-link.git
commit
6659f0ed21
@ -0,0 +1,114 @@ |
|||||||
|
#!/usr/bin/perl |
||||||
|
|
||||||
|
use strict; |
||||||
|
use Data::Dumper; |
||||||
|
|
||||||
|
my $dir = shift @ARGV; |
||||||
|
my $out = shift @ARGV; |
||||||
|
|
||||||
|
my $espfs = ''; |
||||||
|
|
||||||
|
my @structured = read_dir_structure($dir, ""); |
||||||
|
|
||||||
|
for my $file (@structured) |
||||||
|
{ |
||||||
|
my $flags = 0; |
||||||
|
my $name = $file; |
||||||
|
my $compression = 0; |
||||||
|
|
||||||
|
if( $name =~ /\.gz$/ ) |
||||||
|
{ |
||||||
|
$flags |= 2; |
||||||
|
$name =~ s/\.gz$//; |
||||||
|
} |
||||||
|
|
||||||
|
my $head = '<!doctype html><html><head><title>esp-link</title><link rel=stylesheet href="/pure.css"><link rel=stylesheet href="/style.css"><meta name=viewport content="width=device-width, initial-scale=1"><script src="/ui.js"></script><script src="/userpage.js"></script></head><body><div id=layout>'; |
||||||
|
|
||||||
|
open IF, "<", "$dir/$file" or die "Can't read file: $!"; |
||||||
|
my @fc = <IF>; |
||||||
|
close(IF); |
||||||
|
my $cnt = join("", @fc); |
||||||
|
|
||||||
|
if( $name =~ /\.html$/ ) |
||||||
|
{ |
||||||
|
if( ! ( $flags & 2 ) ) |
||||||
|
{ |
||||||
|
$cnt = "$head$cnt"; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
printf("TODO: prepend headers to GZipped HTML content!\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$name .= chr(0); |
||||||
|
$name .= chr(0) while( (length($name) & 3) != 0 ); |
||||||
|
|
||||||
|
my $size = length($cnt); |
||||||
|
|
||||||
|
$espfs .= "ESfs"; |
||||||
|
$espfs .= chr($flags); |
||||||
|
$espfs .= chr($compression); |
||||||
|
$espfs .= chr( length($name) & 255 ); |
||||||
|
$espfs .= chr( length($name) / 256 ); |
||||||
|
$espfs .= chr( $size & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x100 ) & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x10000 ) & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x1000000 ) & 255 ); |
||||||
|
$espfs .= chr( $size & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x100 ) & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x10000 ) & 255 ); |
||||||
|
$espfs .= chr( ( $size / 0x1000000 ) & 255 ); |
||||||
|
|
||||||
|
$espfs .= $name; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$cnt .= chr(0) while( (length($cnt) & 3) != 0 ); |
||||||
|
$espfs .= $cnt; |
||||||
|
} |
||||||
|
|
||||||
|
$espfs .= "ESfs"; |
||||||
|
$espfs .= chr(1); |
||||||
|
for(my $i=0; $i < 11; $i++) |
||||||
|
{ |
||||||
|
$espfs .= chr(0); |
||||||
|
} |
||||||
|
|
||||||
|
open FH, ">", $out or die "Can't open file for write, $!"; |
||||||
|
print FH $espfs; |
||||||
|
close(FH); |
||||||
|
|
||||||
|
|
||||||
|
exit(0); |
||||||
|
|
||||||
|
sub read_dir_structure |
||||||
|
{ |
||||||
|
my ($dir, $base) = @_; |
||||||
|
|
||||||
|
my @files; |
||||||
|
|
||||||
|
opendir my $dh, $dir or die "Could not open '$dir' for reading: $!\n"; |
||||||
|
|
||||||
|
while (my $file = readdir $dh) { |
||||||
|
if ($file eq '.' or $file eq '..') { |
||||||
|
next; |
||||||
|
} |
||||||
|
|
||||||
|
my $path = "$dir/$file"; |
||||||
|
if( -d "$path" ) |
||||||
|
{ |
||||||
|
my @sd = read_dir_structure($path, "$base/$file"); |
||||||
|
push @files, @sd ; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
push @files, "$base/$file"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
close( $dh ); |
||||||
|
|
||||||
|
$_ =~ s/^\/// for(@files); |
||||||
|
return @files; |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
#! /bin/bash |
||||||
|
# |
||||||
|
# Flash an AVR with optiboot using the esp-link built-in programmer |
||||||
|
# Basically we first reset the AVR and get in sync, and then send the hex file |
||||||
|
# |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# "THE BEER-WARE LICENSE" (Revision 42): |
||||||
|
# Thorsten von Eicken wrote this file. As long as you retain |
||||||
|
# this notice you can do whatever you want with this stuff. If we meet some day, |
||||||
|
# and you think this stuff is worth it, you can buy me a beer in return. |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
show_help() { |
||||||
|
cat <<EOT |
||||||
|
Usage: ${0##*/} [-options...] hostname sketch.hex |
||||||
|
Flash the AVR running optiboot attached to esp-link with the sketch. |
||||||
|
-v Be verbose |
||||||
|
-h show this help |
||||||
|
|
||||||
|
Example: ${0##*/} -v esp-link mysketch.hex |
||||||
|
${0##*/} 192.168.4.1 mysketch.hex |
||||||
|
EOT |
||||||
|
} |
||||||
|
|
||||||
|
if ! which curl >/dev/null; then |
||||||
|
echo "ERROR: Cannot find curl: it is required for this script." >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
start=`date +%s` |
||||||
|
|
||||||
|
# ===== Parse arguments |
||||||
|
|
||||||
|
verbose= |
||||||
|
|
||||||
|
while getopts "hvx:" opt; do |
||||||
|
case "$opt" in |
||||||
|
h) show_help; exit 0 ;; |
||||||
|
v) verbose=1 ;; |
||||||
|
x) foo="$OPTARG" ;; |
||||||
|
'?') show_help >&2; exit 1 ;; |
||||||
|
esac |
||||||
|
done |
||||||
|
|
||||||
|
# Shift off the options and optional --. |
||||||
|
shift "$((OPTIND-1))" |
||||||
|
|
||||||
|
# Get the fixed arguments |
||||||
|
if [[ $# != 2 ]]; then |
||||||
|
show_help >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
hostname=$1 |
||||||
|
hex=$2 |
||||||
|
|
||||||
|
re='[-A-Za-z0-9.]+' |
||||||
|
if [[ ! "$hostname" =~ $re ]]; then |
||||||
|
echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -r "$hex" ]]; then |
||||||
|
echo "ERROR: cannot read hex file ($hex)" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
# ===== Get AVR in sync |
||||||
|
|
||||||
|
[[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgm/sync" >&2 |
||||||
|
v=; [[ -n "$verbose" ]] && v=-v |
||||||
|
sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgm/sync"` |
||||||
|
if [[ $? != 0 || "$sync" != 204 ]]; then |
||||||
|
echo "Error resetting AVR" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
while true; do |
||||||
|
sync=`curl -m 10 $v -s "http://$hostname/pgm/sync"` |
||||||
|
if [[ $? != 0 ]]; then |
||||||
|
echo "Error checking sync" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
case "$sync" in |
||||||
|
SYNC*) |
||||||
|
echo "AVR in $sync" >&2 |
||||||
|
break;; |
||||||
|
"NOT READY"*) |
||||||
|
[[ -n "$verbose" ]] && echo " Waiting for sync..." >&2 |
||||||
|
;; |
||||||
|
*) |
||||||
|
echo "Error checking sync: $sync" >&2 |
||||||
|
exit 1 |
||||||
|
;; |
||||||
|
esac |
||||||
|
sleep 0.1 |
||||||
|
done |
||||||
|
|
||||||
|
# ===== Send HEX file |
||||||
|
|
||||||
|
[[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2 |
||||||
|
sync=`curl -m 10 $v -s -g -d "@$hex" "http://$hostname/pgm/upload"` |
||||||
|
echo $sync |
||||||
|
if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then |
||||||
|
echo "Error programming AVR" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
sec=$(( `date +%s` - $start )) |
||||||
|
echo "Success, took $sec seconds" >&2 |
||||||
|
exit 0 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,133 @@ |
|||||||
|
#! /bin/bash |
||||||
|
# |
||||||
|
# Flash an esp8266 over wifi. This communicates with the esphttpd's /flash handlers |
||||||
|
# and POSTS the correct binary depending on the parittion that needs to be flashed |
||||||
|
# next. |
||||||
|
# |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
# "THE BEER-WARE LICENSE" (Revision 42): |
||||||
|
# Thorsten von Eicken wrote this file. As long as you retain |
||||||
|
# this notice you can do whatever you want with this stuff. If we meet some day, |
||||||
|
# and you think this stuff is worth it, you can buy me a beer in return. |
||||||
|
# ---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
show_help() { |
||||||
|
cat <<EOT |
||||||
|
Usage: ${0##*/} [-options...] hostname user1.bin user2.bin |
||||||
|
Flash the esp8266 running esphttpd at <hostname> with either <user1.bin> or <user2.bin> |
||||||
|
depending on its current state. Reboot the esp8266 after flashing and wait for it to come |
||||||
|
up again. |
||||||
|
-v Be verbose |
||||||
|
-h show this help |
||||||
|
|
||||||
|
Example: ${0##*/} -v esp8266 firmware/user1.bin firmware/user2.bin |
||||||
|
${0##*/} 192.168.4.1 firmware/user1.bin firmware/user2.bin |
||||||
|
EOT |
||||||
|
} |
||||||
|
|
||||||
|
if ! which curl >/dev/null; then |
||||||
|
echo "ERROR: Cannot find curl: it is required for this script." >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
start=`date +%s` |
||||||
|
|
||||||
|
# ===== Parse arguments |
||||||
|
|
||||||
|
verbose= |
||||||
|
|
||||||
|
while getopts "hvx:" opt; do |
||||||
|
case "$opt" in |
||||||
|
h) show_help; exit 0 ;; |
||||||
|
v) verbose=1 ;; |
||||||
|
x) foo="$OPTARG" ;; |
||||||
|
'?') show_help >&2; exit 1 ;; |
||||||
|
esac |
||||||
|
done |
||||||
|
|
||||||
|
# Shift off the options and optional --. |
||||||
|
shift "$((OPTIND-1))" |
||||||
|
|
||||||
|
# Get the fixed arguments |
||||||
|
if [[ $# != 3 ]]; then |
||||||
|
show_help >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
hostname=$1 |
||||||
|
user1=$2 |
||||||
|
user2=$3 |
||||||
|
|
||||||
|
re='[-A-Za-z0-9.]+' |
||||||
|
if [[ ! "$hostname" =~ $re ]]; then |
||||||
|
echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -r "$user1" ]]; then |
||||||
|
echo "ERROR: cannot read user1 firmware file ($user1)" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ ! -r "$user2" ]]; then |
||||||
|
echo "ERROR: cannot read user2 firmware file ($user2)" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
# ===== Retrieve the 'next' firmware required |
||||||
|
|
||||||
|
fw= |
||||||
|
while true; do |
||||||
|
[[ -n "$verbose" ]] && echo "Fetching http://$hostname/flash/next" >&2 |
||||||
|
v=; [[ -n "$verbose" ]] && v=-v |
||||||
|
next=`curl -m 10 $v -s "http://$hostname/flash/next"` |
||||||
|
if [[ $? != 0 ]]; then |
||||||
|
echo "Error retrieving http://$hostname/flash/next" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
case "$next" in |
||||||
|
user1.bin) |
||||||
|
echo "Flashing user1.bin" >&2 |
||||||
|
fw="$user1" |
||||||
|
break;; |
||||||
|
user2.bin) |
||||||
|
echo "Flashing user2.bin" >&2 |
||||||
|
fw="$user2" |
||||||
|
break;; |
||||||
|
*) |
||||||
|
echo "Error retrieving or parsing http://$hostname/flash/next" >&2 |
||||||
|
exit 1 |
||||||
|
;; |
||||||
|
esac |
||||||
|
done |
||||||
|
|
||||||
|
#silent=-s |
||||||
|
[[ -n "$verbose" ]] && silent= |
||||||
|
res=`curl $silent -XPOST --data-binary "@$fw" "http://$hostname/flash/upload"` |
||||||
|
if [[ $? != 0 ]]; then |
||||||
|
echo "Error flashing $fw" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
sleep 2 |
||||||
|
echo "Rebooting into new firmware" >&2 |
||||||
|
curl -m 10 -s "http://$hostname/flash/reboot" |
||||||
|
|
||||||
|
sleep 2 |
||||||
|
echo "Waiting for ESP8266 to come back" |
||||||
|
while true; do |
||||||
|
[[ -n "$verbose" ]] && echo "Fetching http://$hostname/flash/next" >&2 |
||||||
|
next2=`curl -m 10 $v -s "http://$hostname/flash/next"` |
||||||
|
[[ -n "$verbose" ]] && echo "got: $next2" |
||||||
|
re='user[12]\.bin' |
||||||
|
if [[ "$next2" =~ $re ]]; then |
||||||
|
if [[ "$next2" != "$next" ]]; then |
||||||
|
sec=$(( `date +%s` - $start )) |
||||||
|
echo "Success, took $sec seconds" >&2 |
||||||
|
exit 0 |
||||||
|
else |
||||||
|
echo "Flashing seems to have failed and it reverted to the old firmware?" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
fi |
||||||
|
sleep 1 |
||||||
|
done |
@ -0,0 +1,432 @@ |
|||||||
|
// Copyright 2016 by BeeGee, see LICENSE.txt
|
||||||
|
//
|
||||||
|
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Mar 4, 2015, Author: Minh
|
||||||
|
// Adapted from: rest.c, Author: Thorsten von Eicken
|
||||||
|
|
||||||
|
#include "esp8266.h" |
||||||
|
#include "c_types.h" |
||||||
|
#include "ip_addr.h" |
||||||
|
#include "socket.h" |
||||||
|
#include "cmd.h" |
||||||
|
|
||||||
|
#define SOCK_DBG |
||||||
|
|
||||||
|
#ifdef SOCK_DBG |
||||||
|
#define DBG_SOCK(format, ...) os_printf(format, ## __VA_ARGS__) |
||||||
|
#else |
||||||
|
#define DBG_SOCK(format, ...) do { } while(0) |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
char *host; |
||||||
|
uint32_t port; |
||||||
|
ip_addr_t ip; |
||||||
|
struct espconn *pCon; |
||||||
|
char *data; |
||||||
|
uint16_t data_len; |
||||||
|
uint16_t data_sent; |
||||||
|
uint32_t resp_cb; |
||||||
|
uint8_t conn_num; |
||||||
|
uint8_t sock_mode; |
||||||
|
} SocketClient; |
||||||
|
|
||||||
|
|
||||||
|
// Connection pool for TCP/UDP socket clients/servers. Attached MCU's just call SOCKET_setup and this allocates
|
||||||
|
// a connection, They never call any 'free' and given that the attached MCU could restart at
|
||||||
|
// any time, we cannot really rely on the attached MCU to call 'free' ever, so better do without.
|
||||||
|
// Instead, we allocate a fixed pool of connections an round-robin. What this means is that the
|
||||||
|
// attached MCU should really use at most as many SOCKET connections as there are slots in the pool.
|
||||||
|
#define MAX_SOCKET 4 |
||||||
|
static SocketClient socketClient[MAX_SOCKET]; |
||||||
|
static uint8_t socketNum = 0xff; // index into socketClient for next slot to allocate
|
||||||
|
|
||||||
|
// Any incoming data?
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socketclient_recv_cb(void *arg, char *pusrdata, unsigned short length) { |
||||||
|
struct espconn *pCon = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pCon->reverse; |
||||||
|
|
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
uint8_t cb_type = USERCB_RECV; |
||||||
|
DBG_SOCK("SOCKET #%d: Received %d bytes: %s\n", client-socketClient, length, pusrdata); |
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 4); |
||||||
|
cmdResponseBody(&cb_type, 1);
|
||||||
|
cmdResponseBody(&clientNum, 1); |
||||||
|
cmdResponseBody(&length, 2); |
||||||
|
cmdResponseBody(pusrdata, length); |
||||||
|
cmdResponseEnd(); |
||||||
|
|
||||||
|
if (client->sock_mode != SOCKET_TCP_SERVER) { // We don't wait for a response
|
||||||
|
DBG_SOCK("SOCKET #%d: disconnect after receiving\n", client-socketClient); |
||||||
|
espconn_disconnect(client->pCon); // disconnect from the server
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Data is sent
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socketclient_sent_cb(void *arg) { |
||||||
|
struct espconn *pCon = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pCon->reverse; |
||||||
|
|
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
uint8_t cb_type = USERCB_SENT; |
||||||
|
DBG_SOCK("SOCKET #%d: Sent\n", client-socketClient); |
||||||
|
sint16 sentDataLen = client->data_sent; |
||||||
|
if (client->data_sent != client->data_len)
|
||||||
|
{ |
||||||
|
// we only sent part of the buffer, send the rest
|
||||||
|
uint16_t data_left = client->data_len - client->data_sent; |
||||||
|
if (data_left > 1400) // we have more than 1400 bytes left
|
||||||
|
{
|
||||||
|
data_left = 1400; |
||||||
|
espconn_sent(client->pCon, (uint8_t*)(client->data+client->data_sent), 1400 ); |
||||||
|
} |
||||||
|
espconn_sent(client->pCon, (uint8_t*)(client->data+client->data_sent), data_left ); |
||||||
|
client->data_sent += data_left; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// we're done sending, free the memory
|
||||||
|
if (client->data) os_free(client->data); |
||||||
|
client->data = 0; |
||||||
|
|
||||||
|
if (client->sock_mode == SOCKET_TCP_CLIENT) { // We don't wait for a response
|
||||||
|
DBG_SOCK("SOCKET #%d: disconnect after sending\n", clientNum); |
||||||
|
espconn_disconnect(client->pCon); |
||||||
|
} |
||||||
|
|
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 3); |
||||||
|
cmdResponseBody(&cb_type, 1);
|
||||||
|
cmdResponseBody(&clientNum, 1); |
||||||
|
cmdResponseBody(&sentDataLen, 2); |
||||||
|
cmdResponseEnd(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Connection is disconnected
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socketclient_discon_cb(void *arg) { |
||||||
|
struct espconn *pespconn = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pespconn->reverse; |
||||||
|
|
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
uint8_t cb_type = USERCB_CONN; |
||||||
|
sint16 _status = CONNSTAT_DIS; |
||||||
|
DBG_SOCK("SOCKET #%d: Disconnect\n", clientNum); |
||||||
|
// free the data buffer, if we have one
|
||||||
|
if (client->data) os_free(client->data); |
||||||
|
client->data = 0; |
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 3); |
||||||
|
cmdResponseBody(&cb_type, 1);
|
||||||
|
cmdResponseBody(&clientNum, 1); |
||||||
|
cmdResponseBody(&_status, 2); |
||||||
|
cmdResponseEnd(); |
||||||
|
} |
||||||
|
|
||||||
|
// Connection was reset
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socketclient_recon_cb(void *arg, sint8 errType) { |
||||||
|
struct espconn *pCon = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pCon->reverse; |
||||||
|
|
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
uint8_t cb_type = USERCB_RECO; |
||||||
|
sint16 _errType = errType; |
||||||
|
os_printf("SOCKET #%d: conn reset, err=%d\n", clientNum, _errType); |
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 3); |
||||||
|
cmdResponseBody(&cb_type, 1);
|
||||||
|
cmdResponseBody(&clientNum, 1); |
||||||
|
cmdResponseBody(&_errType, 2); |
||||||
|
cmdResponseEnd(); |
||||||
|
// free the data buffer, if we have one
|
||||||
|
if (client->data) os_free(client->data); |
||||||
|
client->data = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Connection is done
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socketclient_connect_cb(void *arg) { |
||||||
|
struct espconn *pCon = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pCon->reverse; |
||||||
|
|
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
uint8_t cb_type = USERCB_CONN; |
||||||
|
sint16 _status = CONNSTAT_CON; |
||||||
|
DBG_SOCK("SOCKET #%d: connected socket mode = %d\n", clientNum, client->sock_mode); |
||||||
|
espconn_regist_disconcb(client->pCon, socketclient_discon_cb); |
||||||
|
espconn_regist_recvcb(client->pCon, socketclient_recv_cb); |
||||||
|
espconn_regist_sentcb(client->pCon, socketclient_sent_cb); |
||||||
|
|
||||||
|
DBG_SOCK("SOCKET #%d: sending %d\n", clientNum, client->data_sent); |
||||||
|
if (client->sock_mode != SOCKET_TCP_SERVER) { // Send data after established connection only in client mode
|
||||||
|
client->data_sent = client->data_len <= 1400 ? client->data_len : 1400; |
||||||
|
DBG_SOCK("SOCKET #%d: sending %d\n", clientNum, client->data_sent); |
||||||
|
espconn_send(client->pCon, (uint8_t*)client->data, client->data_sent); |
||||||
|
} |
||||||
|
|
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 3); |
||||||
|
cmdResponseBody(&cb_type, 1);
|
||||||
|
cmdResponseBody(&clientNum, 1); |
||||||
|
cmdResponseBody(&_status, 2); |
||||||
|
cmdResponseEnd(); |
||||||
|
} |
||||||
|
|
||||||
|
static void ICACHE_FLASH_ATTR |
||||||
|
socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { |
||||||
|
struct espconn *pConn = (struct espconn *)arg; |
||||||
|
SocketClient* client = (SocketClient *)pConn->reverse; |
||||||
|
uint8_t clientNum = client->conn_num; |
||||||
|
|
||||||
|
if(ipaddr == NULL) { |
||||||
|
sint16 _errType = ESPCONN_RTE; //-4;
|
||||||
|
uint8_t cb_type = USERCB_RECO; // use Routing problem or define a new one
|
||||||
|
os_printf("SOCKET #%d DNS: Got no ip, report error\n", clientNum); |
||||||
|
cmdResponseStart(CMD_RESP_CB, client->resp_cb, 3);
|
||||||
|
cmdResponseBody(&cb_type, 2); // Same as connection reset?? or define a new one
|
||||||
|
cmdResponseBody(&clientNum, 1);
|
||||||
|
cmdResponseBody(&_errType, 2);
|
||||||
|
cmdResponseEnd();
|
||||||
|
return; |
||||||
|
} |
||||||
|
DBG_SOCK("SOCKET #%d DNS: found ip %d.%d.%d.%d\n", |
||||||
|
clientNum, |
||||||
|
*((uint8 *) &ipaddr->addr), |
||||||
|
*((uint8 *) &ipaddr->addr + 1), |
||||||
|
*((uint8 *) &ipaddr->addr + 2), |
||||||
|
*((uint8 *) &ipaddr->addr + 3)); |
||||||
|
if(client->ip.addr == 0 && ipaddr->addr != 0) { |
||||||
|
os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); |
||||||
|
espconn_connect(client->pCon); |
||||||
|
DBG_SOCK("SOCKET #%d: connecting...\n", clientNum); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
SOCKET_Setup(CmdPacket *cmd) { |
||||||
|
CmdRequest req; |
||||||
|
uint16_t port; |
||||||
|
uint8_t sock_mode; |
||||||
|
int32_t err = -1; // error code in case of failure
|
||||||
|
|
||||||
|
// start parsing the command
|
||||||
|
cmdRequest(&req, cmd); |
||||||
|
if(cmdGetArgc(&req) != 3) { |
||||||
|
DBG_SOCK("SOCKET Setup parse command failure: (cmdGetArgc(&req) != 3)\n"); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
err--; |
||||||
|
|
||||||
|
// get the hostname (IP address)
|
||||||
|
uint16_t len = cmdArgLen(&req); |
||||||
|
if (len > 128) { |
||||||
|
DBG_SOCK("SOCKET Setup parse command failure: hostname longer than 128 characters\n"); |
||||||
|
goto fail; // safety check
|
||||||
|
} |
||||||
|
err--; |
||||||
|
uint8_t *socket_host = (uint8_t*)os_zalloc(len + 1); |
||||||
|
if (socket_host == NULL) { |
||||||
|
DBG_SOCK("SOCKET Setup failed to alloc memory for socket_host\n"); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
if (cmdPopArg(&req, socket_host, len)) { |
||||||
|
DBG_SOCK("SOCKET Setup parse command failure: (cmdPopArg(&req, socket_host, len))\n"); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
err--; |
||||||
|
socket_host[len] = 0; |
||||||
|
|
||||||
|
// get the port
|
||||||
|
if (cmdPopArg(&req, (uint8_t*)&port, 2)) { |
||||||
|
DBG_SOCK("SOCKET Setup parse command failure: cannot get port\n"); |
||||||
|
os_free(socket_host); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
err--; |
||||||
|
|
||||||
|
// get the socket mode
|
||||||
|
if (cmdPopArg(&req, (uint8_t*)&sock_mode, 1)) { |
||||||
|
DBG_SOCK("SOCKET Setup parse command failure: cannot get mode\n"); |
||||||
|
os_free(socket_host); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
err--; |
||||||
|
DBG_SOCK("SOCKET Setup listener flag\n"); |
||||||
|
|
||||||
|
// clear connection structures the first time
|
||||||
|
if (socketNum == 0xff) { |
||||||
|
os_memset(socketClient, 0, MAX_SOCKET * sizeof(SocketClient)); |
||||||
|
socketNum = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// allocate a connection structure
|
||||||
|
SocketClient *client = socketClient + socketNum; |
||||||
|
uint8_t clientNum = socketNum; |
||||||
|
socketNum = (socketNum+1)%MAX_SOCKET; |
||||||
|
|
||||||
|
// free any data structure that may be left from a previous connection
|
||||||
|
if (client->data) os_free(client->data); |
||||||
|
if (client->pCon) { |
||||||
|
if (sock_mode != SOCKET_UDP) { |
||||||
|
if (client->pCon->proto.tcp) os_free(client->pCon->proto.tcp); |
||||||
|
} else { |
||||||
|
if (client->pCon->proto.udp) os_free(client->pCon->proto.udp); |
||||||
|
} |
||||||
|
os_free(client->pCon); |
||||||
|
} |
||||||
|
os_memset(client, 0, sizeof(SocketClient)); |
||||||
|
DBG_SOCK("SOCKET #%d: Setup host=%s port=%d \n", clientNum, socket_host, port); |
||||||
|
|
||||||
|
client->sock_mode = sock_mode; |
||||||
|
client->resp_cb = cmd->value; |
||||||
|
client->conn_num = clientNum; |
||||||
|
|
||||||
|
client->host = (char *)socket_host; |
||||||
|
client->port = port; |
||||||
|
|
||||||
|
if (sock_mode == SOCKET_UDP) { |
||||||
|
wifi_set_broadcast_if(STATIONAP_MODE); |
||||||
|
} |
||||||
|
|
||||||
|
client->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); |
||||||
|
if (client->pCon == NULL) { |
||||||
|
DBG_SOCK("SOCKET #%d: Setup failed to alloc memory for client_pCon\n", clientNum); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
if (sock_mode != SOCKET_UDP) { |
||||||
|
client->pCon->type = ESPCONN_TCP; |
||||||
|
client->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); |
||||||
|
if (client->pCon->proto.tcp == NULL) { |
||||||
|
DBG_SOCK("SOCKET #%d: Setup failed to alloc memory for client->pCon->proto.tcp\n", clientNum); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
} else { |
||||||
|
client->pCon->type = ESPCONN_UDP; |
||||||
|
client->pCon->proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp)); |
||||||
|
if (client->pCon->proto.udp == NULL) { |
||||||
|
DBG_SOCK("SOCKET #%d: Setup failed to alloc memory for client->pCon->proto.udp\n", clientNum); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
} |
||||||
|
client->pCon->state = ESPCONN_NONE; |
||||||
|
|
||||||
|
os_memcpy(client->host, socket_host, 4); |
||||||
|
if (sock_mode != SOCKET_UDP) { |
||||||
|
client->pCon->proto.tcp->remote_port = client->port; |
||||||
|
client->pCon->proto.tcp->local_port = client->port; // espconn_port();
|
||||||
|
} else { |
||||||
|
client->pCon->proto.udp->remote_port = client->port; |
||||||
|
client->pCon->proto.udp->local_port = client->port; |
||||||
|
} |
||||||
|
|
||||||
|
client->pCon->reverse = client; |
||||||
|
|
||||||
|
espconn_regist_sentcb(client->pCon, socketclient_sent_cb); |
||||||
|
espconn_regist_recvcb(client->pCon, socketclient_recv_cb); |
||||||
|
if (sock_mode == SOCKET_UDP) { |
||||||
|
DBG_SOCK("SOCKET #%d: Create connection to ip %s:%d\n", clientNum, client->host, client->port); |
||||||
|
|
||||||
|
if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.udp->remote_ip)) { |
||||||
|
espconn_create(client->pCon); |
||||||
|
} else { |
||||||
|
DBG_SOCK("SOCKET #%d: failed to copy remote_ip to &client->pCon->proto.udp->remote_ip\n", clientNum); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
} else { |
||||||
|
espconn_regist_reconcb(client->pCon, socketclient_recon_cb); |
||||||
|
if (client->sock_mode == SOCKET_TCP_SERVER) { // Server mode?
|
||||||
|
DBG_SOCK("SOCKET #%d: Enable server mode on port%d\n", clientNum, client->port); |
||||||
|
espconn_accept(client->pCon); |
||||||
|
espconn_regist_connectcb(client->pCon, socketclient_connect_cb); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cmdResponseStart(CMD_RESP_V, clientNum, 0); |
||||||
|
cmdResponseEnd(); |
||||||
|
DBG_SOCK("SOCKET #%d: setup finished\n", clientNum); |
||||||
|
return; |
||||||
|
|
||||||
|
fail: |
||||||
|
cmdResponseStart(CMD_RESP_V, err, 0); |
||||||
|
cmdResponseEnd(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
SOCKET_Send(CmdPacket *cmd) { |
||||||
|
CmdRequest req; |
||||||
|
cmdRequest(&req, cmd); |
||||||
|
|
||||||
|
// Get client
|
||||||
|
uint32_t clientNum = cmd->value; |
||||||
|
SocketClient *client = socketClient + (clientNum % MAX_SOCKET); |
||||||
|
DBG_SOCK("SOCKET #%d: send", clientNum); |
||||||
|
|
||||||
|
if (cmd->argc != 1 && cmd->argc != 2) { |
||||||
|
DBG_SOCK("\nSOCKET #%d: send - wrong number of arguments\n", clientNum); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Get data to sent
|
||||||
|
client->data_len = cmdArgLen(&req); |
||||||
|
DBG_SOCK(" dataLen=%d", client->data_len); |
||||||
|
|
||||||
|
if (client->data) os_free(client->data); |
||||||
|
client->data = (char*)os_zalloc(client->data_len); |
||||||
|
if (client->data == NULL) { |
||||||
|
DBG_SOCK("\nSOCKET #%d failed to alloc memory for client->data\n", clientNum); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
cmdPopArg(&req, client->data, client->data_len); |
||||||
|
DBG_SOCK(" socketData=%s", client->data); |
||||||
|
|
||||||
|
// client->data_len = os_sprintf((char*)client->data, socketDataSet, socketData);
|
||||||
|
|
||||||
|
DBG_SOCK("\n"); |
||||||
|
|
||||||
|
DBG_SOCK("SOCKET #%d: Create connection to ip %s:%d\n", clientNum, client->host, client->port); |
||||||
|
|
||||||
|
if (client->sock_mode == SOCKET_TCP_SERVER) { // In TCP server mode we should be connected already and send the data immediately
|
||||||
|
remot_info *premot = NULL; |
||||||
|
if (espconn_get_connection_info(client->pCon,&premot,0) == ESPCONN_OK){ |
||||||
|
for (uint8 count = 0; count < client->pCon->link_cnt; count ++){ |
||||||
|
client->pCon->proto.tcp->remote_port = premot[count].remote_port; |
||||||
|
|
||||||
|
client->pCon->proto.tcp->remote_ip[0] = premot[count].remote_ip[0]; |
||||||
|
client->pCon->proto.tcp->remote_ip[1] = premot[count].remote_ip[1]; |
||||||
|
client->pCon->proto.tcp->remote_ip[2] = premot[count].remote_ip[2]; |
||||||
|
client->pCon->proto.tcp->remote_ip[3] = premot[count].remote_ip[3]; |
||||||
|
DBG_SOCK("SOCKET #%d: connected to %d.%d.%d.%d:%d\n",
|
||||||
|
clientNum, |
||||||
|
client->pCon->proto.tcp->remote_ip[0], |
||||||
|
client->pCon->proto.tcp->remote_ip[1], |
||||||
|
client->pCon->proto.tcp->remote_ip[2], |
||||||
|
client->pCon->proto.tcp->remote_ip[3], |
||||||
|
client->pCon->proto.tcp->remote_port |
||||||
|
); |
||||||
|
} |
||||||
|
client->data_sent = client->data_len <= 1400 ? client->data_len : 1400; |
||||||
|
DBG_SOCK("SOCKET #%d: Server sending %d\n", clientNum, client->data_sent); |
||||||
|
espconn_send(client->pCon, (uint8_t*)client->data, client->data_sent); |
||||||
|
} |
||||||
|
} else if (client->sock_mode != SOCKET_UDP) { // In TCP client mode we connect and send the data from the connected callback
|
||||||
|
espconn_regist_connectcb(client->pCon, socketclient_connect_cb); |
||||||
|
|
||||||
|
if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) { |
||||||
|
DBG_SOCK("SOCKET #%d: Connect to ip %s:%d\n", clientNum, client->host, client->port); |
||||||
|
espconn_connect(client->pCon); |
||||||
|
} else { |
||||||
|
DBG_SOCK("SOCKET #%d: Connect to host %s:%d\n", clientNum, client->host, client->port); |
||||||
|
espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, socket_dns_found); |
||||||
|
}
|
||||||
|
} else { // in UDP socket mode we send the data immediately
|
||||||
|
client->data_sent = client->data_len <= 1400 ? client->data_len : 1400; |
||||||
|
DBG_SOCK("SOCKET #%d: sending %d bytes: %s\n", clientNum, client->data_sent, client->data); |
||||||
|
espconn_sent(client->pCon, (uint8_t*)client->data, client->data_sent); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
|
||||||
|
fail: |
||||||
|
DBG_SOCK("\n"); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/*
|
||||||
|
* socket.h |
||||||
|
* |
||||||
|
* Created on: Sep 16th 2016 |
||||||
|
* Author: BeeGee |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef MODULES_SOCKET_H_ |
||||||
|
#define MODULES_SOCKET_H_ |
||||||
|
|
||||||
|
#include "cmd.h" |
||||||
|
|
||||||
|
void SOCKET_Setup(CmdPacket *cmd); |
||||||
|
void SOCKET_Send(CmdPacket *cmd); |
||||||
|
|
||||||
|
// Socket mode
|
||||||
|
typedef enum { |
||||||
|
SOCKET_TCP_CLIENT = 0, /**< TCP socket client for sending only, doesn't wait for response from server */ |
||||||
|
SOCKET_TCP_CLIENT_LISTEN, /**< TCP socket client, waits for response from server after sending */ |
||||||
|
SOCKET_TCP_SERVER, /**< TCP socket server */ |
||||||
|
SOCKET_UDP, /**< UDP socket for sending and receiving UDP packets */ |
||||||
|
} socketMode; |
||||||
|
|
||||||
|
// Callback type
|
||||||
|
typedef enum { |
||||||
|
USERCB_SENT = 0, /**< Data send finished */ |
||||||
|
USERCB_RECV, /**< Data received */ |
||||||
|
USERCB_RECO, /**< Connection error */ |
||||||
|
USERCB_CONN, /**< Connection event */ |
||||||
|
} cbType; |
||||||
|
|
||||||
|
// Connection status
|
||||||
|
typedef enum { |
||||||
|
CONNSTAT_DIS = 0, // Disconnected
|
||||||
|
CONNSTAT_CON, // Connected
|
||||||
|
} connStat; |
||||||
|
|
||||||
|
#endif /* MODULES_SOCKET_H_ */ |
Loading…
Reference in new issue