Merge branch 'gtxaspec:master' into master

This commit is contained in:
sideup66
2022-07-31 15:35:23 -04:00
committed by GitHub
38 changed files with 1117 additions and 80 deletions

View File

@@ -16,6 +16,7 @@ echo "CAR: c: reverse right"
echo "CAR: x: all stop"
echo "CAR: h: headlight on/off"
echo "CAR: j: irled on/off"
echo "CAR: b: honk"
echo -e ""
echo "CAR: 1: quit ASAP!"
echo -e ""
@@ -133,6 +134,12 @@ elif [ "$input" = "h" ]; then
elif [ "$input" = "j" ]; then
irled
elif [ "$input" = "x" ]; then
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$input" = "b" ]; then
/opt/wz_mini/bin/cmd aplay /opt/wz_mini/usr/share/audio/honk.wav 70 > /dev/null 2>&1 &
elif [ "$input" = "1" ]; then
#exit
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0

Binary file not shown.

View File

@@ -55,6 +55,8 @@ cp /system/bin/app.ver /opt/wz_mini/tmp/log_gather/system_app.ver
echo "gather mmc info"
cat /sys/bus/mmc/devices/mmc1\:0001/mmc1\:0001\:1/* > /opt/wz_mini/tmp/log_gather/mmc_sys.log
echo "gather lsusb"
lsusb > /opt/wz_mini/tmp/log_gather/lsusb.log
if [ -f /tmp/sd_check_result.txt ]; then
echo "copy sd_check_result.txt"

View File

@@ -0,0 +1,3 @@
#!/bin/sh
nano /opt/wz_mini/wz_mini.conf

View File

@@ -8,8 +8,10 @@ domain-needed
bogus-priv
dns-forward-max=150
cache-size=1000
min-cache-ttl=86400
neg-ttl=3600
no-poll
resolv-file=/opt/wz_mini/etc/resolv.dnsmasq
addn-hosts=/opt/wz_mini/etc/hosts
#log-facility=-
#log-queries

View File

@@ -0,0 +1 @@
127.0.0.1 localhost

View File

@@ -48,18 +48,62 @@ wait_wlan() {
##Check if the driver has been loaded for the onboard wlan0, store the MAC.
while true
do
if ifconfig wlan0 | grep "inet addr"; then
if ifconfig wlan0 | grep "HWaddr"; then
echo "wlan0 hwaddr is up"
store_mac
break
elif ifconfig wlan0 | grep "inet addr" && [[ "$ENABLE_USB_ETH" == "true" || "$ENABLE_USB_DIRECT" == "true" ]]; then
store_mac
break
fi
echo " wlan0 not ready yet..."
else
echo "wlan0 hwaddr not ready yet..."
sleep 5
fi
done
}
wpa_check() {
##Check if wpa_supplicant has been created by iCamera
if [ -e /tmp/wpa_supplicant.conf ]; then
wait_wlan
echo "wpa_supplicant.conf ready"
else
echo "wpa_supplicant.conf not ready, wait some time for creation."
COUNT=0
ATTEMPTS=15
until [[ -e /tmp/wpa_supplicant.conf ]] || [[ $COUNT -eq $ATTEMPTS ]]; do
echo -e "$(( COUNT++ ))... \c"
sleep 5
wpa_check
done
if [[ $COUNT -eq $ATTEMPTS ]]; then
echo "time exceeded waiting for iCamera, continue potentially broken condition without network."
fi
fi
}
wlanold_check() {
#Have we renamed interfaces yet?
if [ -d /sys/class/net/wlanold ]; then
echo "wlanold exist"
eth_wlan_up
else
echo "wlanold doesn't exist"
if [[ "$BONDING_ENABLED" == "true" ]] && ([[ "$ENABLE_USB_ETH" == "true" ]] || [[ "$ENABLE_USB_DIRECT" == "true" ]]); then
rename_interface_and_setup_bonding bond0 "$BONDING_PRIMARY_INTERFACE" "$BONDING_SECONDARY_INTERFACE"
else
rename_interface $1
fi
fi
}
netloop() {
##While loop for check
while true
do
wlanold_check $1
echo "wlan0 not ready yet..."
sleep 5
done
}
rename_interface() {
##Fool iCamera by renaming the hardline interface to wlan0
echo "renaming interfaces"
@@ -166,48 +210,6 @@ eth_wlan_up() {
break
}
wpa_check() {
#Check if wpa_supplicant has been created by iCamera
if [ -e /tmp/wpa_supplicant.conf ]; then
echo "wpa_supplicant.conf ready"
wlanold_check $1
else
echo "wpa_supplicant.conf not ready, wait some time for creation."
COUNT=0
ATTEMPTS=15
until [[ -e /tmp/wpa_supplicant.conf ]] || [[ $COUNT -eq $ATTEMPTS ]]; do
echo -e "$(( COUNT++ ))... \c"
sleep 5
done
[[ $COUNT -eq $ATTEMPTS ]] && echo "time exceeded waiting for iCamera, continue potentially broken condition without network." && wlanold_check $1
fi
}
wlanold_check() {
#Have we renamed interfaces yet?
if [ -d /sys/class/net/wlanold ]; then
echo "wlanold exist"
eth_wlan_up
else
echo "wlanold doesn't exist"
if [[ "$BONDING_ENABLED" == "true" ]] && ([[ "$ENABLE_USB_ETH" == "true" ]] || [[ "$ENABLE_USB_DIRECT" == "true" ]]); then
rename_interface_and_setup_bonding bond0 "$BONDING_PRIMARY_INTERFACE" "$BONDING_SECONDARY_INTERFACE"
else
rename_interface $1
fi
fi
}
netloop() {
##While loop for check
while true
do
wpa_check $1
echo "wlan0 not ready yet..."
sleep 5
done
}
swap_enable() {
if [ -e /opt/wz_mini/swap ]; then
echo "Swap file exists"
@@ -249,7 +251,7 @@ done
}
first_run_check
wait_wlan
wpa_check
#Set module dir depending on platform
if [ -f /opt/wz_mini/tmp/.T20 ]; then
@@ -383,13 +385,8 @@ if [[ "$ENABLE_USB_DIRECT" == "true" ]]; then
swap_enable
#loop begin
while true
do
wpa_check usb0
echo "wlan0 not ready yet..."
sleep 1
done
netloop usb0
else
echo "USB Direct disabled"
fi
@@ -407,13 +404,8 @@ if [[ "$ENABLE_USB_RNDIS" == "true" ]]; then
swap_enable
#loop begin
while true
do
wpa_check usb0
echo "wlan0 not ready yet..."
sleep 1
done
netloop usb0
fi
else
echo "usb rndis disabled"
@@ -593,6 +585,16 @@ if [[ "$NIGHT_DROP_DISABLE" == "true" ]]; then
touch /opt/wz_mini/tmp/.nd
fi
if [[ "$ENABLE_LOCAL_DNS" == "true" ]]; then
dnsmasq -C /opt/wz_mini/etc/dnsmasq.conf
rm -f /tmp/resolv.conf
cp /opt/wz_mini/etc/resolv.conf /tmp/resolv.conf
fi
if [[ "$WEB_SERVER_ENABLED" == "true" ]]; then
httpd -p 80 -h /opt/wz_mini/www
fi
hostname_set
touch /opt/wz_mini/tmp/.wz_user_firstrun
pkill -f dumpload #Kill dumpload so it won't waste cpu or ram gathering cores and uploading them when something crashes

View File

@@ -0,0 +1 @@
root:x:0:0:root:/opt/wz_mini/root:/bin/sh

View File

@@ -0,0 +1 @@
nameserver 127.0.0.1

View File

@@ -1,4 +1,4 @@
#List your desired upstream dns servers here
#nameserver 8.8.8.8
#nameserver 8.8.4.4
nameserver 1.1.1.1
nameserver 1.0.0.1

View File

@@ -92,6 +92,7 @@ NIGHT_DROP_DISABLE="false"
DISABLE_MOTOR="false"
ENABLE_FSCK_ON_BOOT="false"
ENABLE_CAR_DRIVER="false"
ENABLE_LOCAL_DNS="false"
#####DEBUG#####
#drops you to a shell via serial, doesn't load app_init.sh
@@ -99,5 +100,8 @@ DEBUG_ENABLED="false"
DEBUG_INITRAMFS_ENABLED="false"
DEBUG_PASSWORD="false"
####WEB####
WEB_SERVER_ENABLED="false"
#####SCRIPTING#####
CUSTOM_SCRIPT_PATH=""

View File

@@ -1 +1 @@
Fri Jul 15 11:46:04 AM PDT 2022
Tue Jul 26 07:57:02 AM PDT 2022

Binary file not shown.

View File

@@ -0,0 +1,31 @@
.wz_car_BUTTON {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
.wz_car_BUTTON_N {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
}
.wz_car_TABLE {
width: 55%;
border-collapse: collapse;
float: left;
}

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="car.js"></script>
<link rel="stylesheet" href="car.css">
</head>
<body>
<h2>wz_mini Car Control</h2>
<table class="wz_car_TABLE" border="1">
<tbody>
<tr style="height: 21px;">
<td style="width: 33.3333%; height: 21px; text-align: left;"><button id="forward_left" class="wz_car_BUTTON">q: forward left</button></td>
<td style="width: 33.3333%; height: 21px; text-align: center;"><button id="forward" class="wz_car_BUTTON">w: forward</button></td>
<td style="width: 33.3333%; height: 21px; text-align: right;"><button id="forward_right" class="wz_car_BUTTON">e: forward right</button></td>
</tr>
<tr style="height: 18px;">
<td style="width: 33.3333%; height: 18px; text-align: left;"><button id="left" class="wz_car_BUTTON">a; left</button></td>
<td style="width: 33.3333%; height: 18px; text-align: center;"><button id="reverse" class="wz_car_BUTTON">s: rear</button></td>
<td style="width: 33.3333%; height: 18px; text-align: right;"><button id="right" class="wz_car_BUTTON">d: right</button></td>
</tr>
<tr style="height: 18px;">
<td style="width: 33.3333%; height: 18px; text-align: left;"><button id="reverse_left" class="wz_car_BUTTON">z: rear left</button></td>
<td style="width: 33.3333%; height: 18px; text-align: center;"><button id="all_stop" class="wz_car_BUTTON">x: all stop</button></td>
<td style="width: 33.3333%; height: 18px; text-align: right;"><button id="reverse_right" class="wz_car_BUTTON">c: rear right</button></td>
</tr>
<td style="width: 33.3333%; height: 18px; text-align: center;">
<button id="headlight_on" class="wz_car_BUTTON_N">h: headlight on</button>
<button id="headlight_off" class="wz_car_BUTTON_N">g: headlight off</button>
</td>
<td style="width: 33.3333%; height: 18px; text-align: center;"><button id="irled_on" class="wz_car_BUTTON">j: ir led on</button><button id="irled_off" class="wz_car_BUTTON">k: ir led off</button></td>
<td style="width: 33.3333%; height: 18px; text-align: right;"><button id="honk" class="wz_car_BUTTON">b: honk</button></td>
</tr>
</tbody>
</table>
<p><img id="car_feed" src="/cgi-bin/jpeg.cgi?channel=1" /></p>
Speed Sleep: <input type="text" size=5 id="sleep_timer" name="sleep_timer" value="0.15">
Car Speed:
<input type="radio" id="speed_fast" class="speed" name="fast" value="fast">
<label for="fast">Fast</label>
<input type="radio" id="speed_slow" class="speed" name="slow" value="slow">
<label for="slow">Slow</label>
<button onclick="set_vars()">Set Variables</button>
</body>
</html>

View File

@@ -0,0 +1,77 @@
const queryString = window.location.search;
console.log(queryString);
const urlParams = new URLSearchParams(queryString);
const speed = urlParams.get('speed')
console.log("speed is", speed);
const sleep_timer = urlParams.get('sleep_time')
console.log("sleep_timer is", sleep_timer);
function set_sleep() {
sleep_timer2 = document.getElementById("sleep_timer").value;
window.location = "car.html?sleep_time=" + sleep_timer2;
}
function set_vars() {
sleep_timer2 = document.getElementById("sleep_timer").value;
var speed = $('input[class="speed"]:checked').val();
window.location = "car.html?sleep_time=" + sleep_timer2 + "&speed=" + speed;
}
var wz_mini_car = {
post: function(action)
{
$.post( "../cgi-bin/car.sh", { speed: speed, action: action, sleep_time: sleep_timer } );
} ,
init: function() {
this.logarray = [];
$("[class*='BUTTON']").on('click',function(e) {
var action = $(this).attr('id');
wz_mini_car.post(action);
});
/* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
switch is strict
*/
addEventListener("keydown", function (e) {
var action = false;
switch(e.key) {
case "w": action = "forward"; break;
case "s": action = "reverse"; break;
case "a": action = "left"; break;
case "d": action = "right"; break;
case "q": action = "forward_left" ; break;
case "e": action = "forward_right"; break;
case "z": action = "reverse_left"; break;
case "c": action = "reverse_right" ; break;
case "x": action = "all_stop" ; break;
case "h": action = "headlight_on" ; break;
case "g": action = "headlight_off" ; break;
case "j": action = "irled_on" ; break;
case "k": action = "irled_off" ; break;
case "b": action = "honk" ; break;
}
if (action) {
wz_mini_car.post(action);
}
});
},
log: function(text)
{
this.logarray.push(text);
}
}
$(document).ready(function() {
wz_mini_car.init();
});
setInterval(function() {
var myImageElement = document.getElementById('car_feed');
myImageElement.src = '/cgi-bin/jpeg.cgi?channel=1&rand=' + Math.random();
}, 1000);

View File

@@ -0,0 +1,23 @@
Network Interface Bonding Support
BONDING_ENABLED="false"
BONDING_PRIMARY_INTERFACE="eth0"
BONDING_SECONDARY_INTERFACE="wlan0"
BONDING_LINK_MONITORING_FREQ_MS="100"
BONDING_DOWN_DELAY_MS="5000"
BONDING_UP_DELAY_MS="5000"
Bonding description is best described here: https://wiki.debian.org/Bonding#Configuration_-_Example_2_.28.22Laptop-Mode.22.29:
("Laptop-Mode")
Tie cable and wireless network interfaces (RJ45/WLAN) together to define a single, virtual (i.e. bonding) network interface (e.g. bond0). As long as the network cable is connected, its interface (e.g. eth0) is used for the network traffic. If you pull the RJ45-plug, ifenslave switches over to the wireless interface (e.g. wlan0) transparently, without any loss of network packages. After reconnecting the network cable, ifenslave switches back to eth0 ("failover mode"). From the outside (=network) view it doesn't matter which interface is active. The bonding device presents its own software-defined (i.e. virtual) MAC address, different from the hardware defined MACs of eth0 or wlan0. The dhcp server will use this MAC to assign an ip address to the bond0 device. So the computer has one unique ip address under which it can be identified. Without bonding each interface would have its own ip address. Currenly supported with ethernet adapters and usb-direct mode.
BONDING_PRIMARY_INTERFACE Specifies the interface that should be the primary. Typically "eth0".
BONDING_SECONDARY_INTERFACE Specifies the interface that should be the secondary. Typically "wlan0".
BONDING_LINK_MONITORING_FREQ_MS Specifies the MII link monitoring frequency in milliseconds. This determines how often the link state of each slave is inspected for link failures.
BONDING_DOWN_DELAY_MS Specifies the time, in milliseconds, to wait before disabling a slave after a link failure has been detected. This option is only valid for the miimon link monitor. The downdelay value should be a multiple of the miimon value; if not, it will be rounded down to the nearest multiple.
BONDING_UP_DELAY_MS Specifies the time, in milliseconds, to wait before enabling a slave after a link recovery has been detected. This option is only valid for the miimon link monitor. The updelay value should be a multiple of the miimon value; if not, it will be rounded down to the nearest multiple.

View File

@@ -0,0 +1,4 @@
If a remote or app update is initiated, the camera will reboot due to the failure of the update. The firmware update should not proceed again for some time, or at all.
When a firmware update is initiated, due to a bootloader issue (bug?), we intercept the update process and flash it manually. This should now result in a successful update, if it doesn't, please restore the unit's firmware manually using demo_wcv3.bin on the micro sd card.

View File

@@ -0,0 +1,4 @@
ENABLE_MP4_WRITE="false"
Forces the camera to skip writing files to /tmp, and write them directly to your storage medium or network mount, prevents trashing. Normally videos are written to /tmp then moved using mv, which can overload camera and or remote network connections. Useful for NFS/CIFS remote video storage.

View File

@@ -0,0 +1,5 @@
the next time you boot your camera, make sure your USB cable is connected to the router. Remember, the camera has to be setup initially with Wi-Fi for this to work. After setup, Wi-Fi is no longer needed. Note that using USB Direct disables the onboard Wi-Fi. Change the MAC Address if you desire via USB_DIRECT_MAC_ADDR variable.
Connectivity is supported using a direct USB connection only... this means a single cable from the camera, to a supported host (An OpenWRT router, for example) that supports the usb-cdc-ncm specification. (NCM, not ECM) If you have an OpenWrt based router, install the kmod-usb-net-cdc-ncm package. The camera should automatically pull the IP from the router with most configurations. You can also use any modern linux distro to provide internet to the camera, provided it supports CDC_NCM. enjoy!
Note: In my testing, the micro-usb cables included with the various cameras do not pass data, so they will not work. Make sure you have a micro-usb cable that passes data!

View File

@@ -0,0 +1,13 @@
USB Ethernet Adapter support:
ENABLE_USB_ETH="true"
ENABLE_USB_ETH_MODULE_AUTODETECT="true"
ENABLE_USB_ETH_MODULE_MANUAL=""
To have the Ethernet NIC be auto-detected and loaded automatically, set the ENABLE_USB_ETH_MODULE_AUTODETECT value to true.
To load a specific USB Ethernet NIC driver, set ENABLE_USB_ETH_MODULE_MANUAL to one of the following: asix, ax88179_178a, cdc_ether, r8152
NOTE: There is a possibility of a conflict between Ethernet NIC adapters that report themselves as PCI ID '0bda:8152'. (Realtek 8152 and CDC Ethernet) Since the 8152 is Realtek's product, that driver will be the one used for products that report that PCI ID. If you happen to have a CDC Ethernet product that uses that specific PCI ID, please set the ENABLE_USB_ETH_MODULE_AUTODETECT option to false, and set ENABLE_USB_ETH_MODULE_MANUAL to "cdc_ether"
The next time you boot your camera, make sure your USB Ethernet Adapter is connected to the camera and ethernet. The camera has to be setup initially with Wi-Fi for this to work. After setup, Wi-Fi is no longer needed, as long as you are using the USB Ethernet Adapter. Note that using USB Ethernet disables the onboard Wi-Fi.

View File

@@ -0,0 +1,6 @@
USB Mass Storage Support:
ENABLE_USB_STORAGE="true"
If you would like to mount an EXT3/4 filesystem, also change:
ENABLE_EXT4="true"

View File

@@ -0,0 +1,19 @@
Wireguard support is available as a kernel module:
ENABLE_WIREGUARD="true"
Use the command wg to setup. See https://www.wireguard.com/quickstart/ for more info.
Some users have asked about tailscale support, I have tested and it works. See the issue #30 for further information.
Example setup:
ENABLE_WIREGUARD="true"
WIREGUARD_IPV4="192.168.2.101/32"
WIREGUARD_PEER_ENDPOINT="x.x.x.x:51820"
WIREGUARD_PEER_PUBLIC_KEY="INSERT_PEER_PUBLIC_KEY_HERE"
WIREGUARD_PEER_ALLOWED_IPS="192.168.2.0/24"
WIREGUARD_PEER_KEEP_ALIVE="25"
To retrieve the public key that you'll need to add the peer to your wireguard endpoint:
Use SSH to log in
wg

View File

@@ -0,0 +1,8 @@
When USB Direct connectivity is enabled, the camera will be unable to communicate with accessories. To enable remote spotlight accessory support, enable the following variable and set the IP Address of the host as follows:
REMOTE_SPOTLIGHT="true"
REMOTE_SPOTLIGHT_HOST="0.0.0.0"
Then, run the following command on the host where the spotlight is attached to:
socat TCP4-LISTEN:9000,reuseaddr,fork /dev/ttyUSB0,raw,echo=0
Change /dev/ttyUSB0 to whatever path your spotlight enumerated to if necessary. The camera will now be able to control the spotlight.

View File

@@ -0,0 +1,10 @@
RTMP Streaming:
RTMP_STREAM_ENABLED="true"
RTMP_STREAM_FEED="video1_unicast"
RMTP_STREAM_SERVICE="youtube"
RTMP_STREAM_DISABLE_AUDIO="false"
RTMP_STREAM_YOUTUBE_KEY="xxx-xxx-xxx-xxx"
RTMP_STREAM_TWITCH_KEY=""
RTMP_STREAM_FACEBOOK_KEY=""
Live stream DIRECTLY from the camera's local RTSP server to: youtube / twitch / facebook live. Audio must be enabled in the RTSP section of the configuration for this to work.

View File

@@ -0,0 +1,25 @@
RTSP streaming: The RTSP server supports the two video streams provided by the camera, 1080p/360p (1296p/480p for the DB3). You can choose to enable a single stream of your choice, or both. Audio is also available. Set your login credentials here, server listening port, and the stream bitrate. (ENC_PARAMETER variable accepts numbers only. 0=FIXQP, 1=CBR, 2=VBR, 4=CAPPED VBR, 8=CAPPED QUALITY. Currently only 2, 4, and 8 are working)
RTSP_LOGIN="admin"
RTSP_PASSWORD=""
RTSP_PORT="8554"
RTSP_HI_RES_ENABLED="true"
RTSP_HI_RES_ENABLE_AUDIO="true"
RTSP_HI_RES_MAX_BITRATE="2048"
RTSP_HI_RES_TARGET_BITRATE="1024"
RTSP_HI_RES_ENC_PARAMETER="2"
RTSP_HI_RES_FPS="15"
RTSP_LOW_RES_ENABLED="false"
RTSP_LOW_RES_ENABLE_AUDIO="false"
RTSP_LOW_RES_MAX_BITRATE=""
RTSP_LOW_RES_TARGET_BITRATE=""
RTSP_LOW_RES_ENC_PARAMETER=""
RTSP_LOW_RES_FPS=""
the singular stream will be located at rtsp://login:password@IP_ADDRESS:8554/unicast multiple streams are located at rtsp://login:password@IP_ADDRESS:8554/video1_unicast and rtsp://login:password@IP_ADDRESS:8554/video2_unicast
Note: If you don't set the password, the password will be set to the unique MAC address of the camera, in all uppercase, including the colons... for example:. AA:BB:CC:00:11:22. It's typically printed on the camera. Higher video bitrates may overload your Wi-Fi connection, so a wired connection is recommended.
Huge credit to @mnakada for his libcallback library: https://github.com/mnakada/atomcam_tools

View File

@@ -0,0 +1,7 @@
Use as a USB Video Class (UVC) Web Camera for your PC is supported. I have tested with Windows 10, Linux, and Android TV, and it appears as a Generic HD Camera. Audio is supported. This mode disables all other functionality, and only works as a USB Web Camera for your PC. Experimental. Note that the cables typically included with the camera do not data, use a known working micro-usb cable which supports data.
Supported modes: MJPG,Video 360p/720p/1080p
WEB_CAM_ENABLE="true"
WEB_CAM_BIT_RATE="8000"
WEB_CAM_FPS_RATE="25"

View File

@@ -0,0 +1,95 @@
#!/bin/sh
set -x
echo "HTTP/1.1 200"
echo -e "Content-type: text/html\n\n"
echo ""
read POST_STRING
SPEED=$(echo $POST_STRING | sed 's/.*speed=//;s/\&.*//')
ACTION=$(echo $POST_STRING | sed 's/.*action=//;s/\&.*//')
SLEEP_TIME=$(echo $POST_STRING | sed 's/.*sleep_time=//;s/\&.*//')
echo "raw post string: $POST_STRING"
echo "speed: $SPEED"
echo "action: $ACTION"
echo "sleep: $SLEEP_TIME"
if [ "$ACTION" = "forward" ]; then
if [ "$SPEED" = "slow" ]; then
echo "slow"
echo -ne "\xaa\x55\x43\x06\x29\x80\xca\x00\x02\xbb" > /dev/ttyUSB0
else
echo "forward"
echo -ne "\xaa\x55\x43\x06\x29\x80\xe3\x00\x02\xd4" > /dev/ttyUSB0
fi
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "reverse" ]; then
echo "reverse"
echo -ne "\xaa\x55\x43\x06\x29\x80\x36\x00\x02\x27" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "left" ]; then
echo "left"
echo -ne "\xaa\x55\x43\x06\x29\x76\x81\x00\x02\x68" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "right" ]; then
echo "right"
echo -ne "\xaa\x55\x43\x06\x29\x8a\x81\x00\x02\x7c" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "forward_left" ]; then
echo "left_forward"
echo -ne "\xaa\x55\x43\x06\x29\x76\xe3\x00\x02\xca" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "forward_right" ]; then
echo "right_forward"
echo -ne "\xaa\x55\x43\x06\x29\x8a\xe3\x00\x02\xde" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "reverse_left" ]; then
echo "left_reverse"
echo -ne "\xaa\x55\x43\x06\x29\x76\x36\x00\x02\x1d" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "reverse_right" ]; then
echo "right_reverse"
echo -ne "\xaa\x55\x43\x06\x29\x8a\x36\x00\x02\x31" > /dev/ttyUSB0
sleep $SLEEP_TIME
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "all_stop" ]; then
echo "all stop"
echo -ne "\xaa\x55\x43\x06\x29\x80\x80\x00\x02\x71" > /dev/ttyUSB0
elif [ "$ACTION" = "headlight_on" ]; then
echo "headlight_on"
echo -ne "\xaa\x55\x43\x04\x1e\x01\x01\x65" > /dev/ttyUSB0
elif [ "$ACTION" = "headlight_off" ]; then
echo "headlight_off"
echo -ne "\xaa\x55\x43\x04\x1e\x02\x01\x66" > /dev/ttyUSB0
elif [ "$ACTION" = "irled_on" ]; then
echo "irled_on"
cmd irled on > /dev/null
elif [ "$ACTION" = "irled_off" ]; then
echo "irled_off"
cmd irled off > /dev/null
elif [ "$ACTION" = "honk" ]; then
echo "honk"
/opt/wz_mini/bin/cmd aplay /opt/wz_mini/usr/share/audio/honk.wav 70 > /dev/null 2>&1 &
else
echo "no input"
fi

View File

@@ -0,0 +1,290 @@
#!/bin/sh
# This serves a rudimentary webpage based on wz_mini.conf
base_dir=/opt/wz_mini/
base_hack_ini=/opt/wz_mini/wz_mini.conf
hack_ini=$base_hack_ini
www_dir=/opt/wz_mini/www/cgi-bin/
camver=V3
camfirmware=$(tail -n1 /configs/app.ver | cut -f2 -d= )
hackver=$(cat /opt/wz_mini/usr/bin/app.ver)
title="Wyze $camver on $camfirmware running wz_mini $hackver as $HOSTNAME"
updated=false
echo "HTTP/1.1 200"
echo -e "Content-type: text/html\n\n"
echo ""
reboot_camera() {
echo "rebooting camera (refreshing screen in 90 seconds)"
echo '<script type="text/javascript">setTimeout(function(){ document.location.reload (); },90 * 1000)</script>'
exit
}
shft() {
# SE loop did not work -- thanks ash!
suff=8
while [ "$suff" -gt 0 ] ;
do
if [[ -f "$1.$suff" ]] ; then
nxt=$((suff + 1))
mv -f "$1.$suff" "$1.$nxt"
fi
suff=$((suff-1))
done
mv -f "$1" "$1.1"
}
function revert_config
{
mv "$hack_ini" "$hack_ini.old"
mv "$hack_ini.$1" "$hack_ini"
}
function revert_menu
{
echo '<h2 id="revert" >Revert Menu</a>'
echo '<div class="old_configs">'
echo 'Prior Versions : '
xuff=0
while [ "$xuff" -lt 9 ] ;
do
xuff=$((xuff + 1))
if [[ -f "$1.$xuff" ]] ; then
filedate=$(date -r "$1.$xuff" )
class=""
if [ "$1.$xuff" = "$2" ];
then
class="current_revert"
fi
echo '<div class="revert_DIV '$class'"><div><a href="?action=show_revert&version='"$xuff"'">'"$xuff </a></div><div> $filedate</div></div>"
fi
done
echo '</div>'
}
function version_info
{
echo "<div id='$1'>"
echo "<div class='ver_DIV' vertype='Camera'>$camver</div>"
echo "<div class='ver_DIV' vertype='Camera Firmware'>$camfirmware</div>"
echo "<div class='ver_DIV' vertype='wz_mini'>$hackver</div>"
echo "<div class='ver_DIV' vertype='Hostname'> $HOSTNAME</div>"
echo "</div>"
}
if [[ $REQUEST_METHOD = 'GET' ]]; then
#since ash does not handle arrays we create variables using eval
IFS='&'
for PAIR in $QUERY_STRING
do
K=$(echo $PAIR | cut -f1 -d=)
VA=$(echo $PAIR | cut -f2 -d=)
eval GET_$K=$VA
done
if [[ "$GET_action" = "reboot" ]]; then
reboot_camera
fi
if [[ "$GET_action" = "revert" ]]; then
revert_config "$GET_version"
fi
if [[ "$GET_action" = "show_revert" ]]; then
hack_ini="$hack_ini.$GET_version"
fi
fi
#test for post
if [[ $REQUEST_METHOD = 'POST' ]]; then
if [ "$CONTENT_LENGTH" -gt 0 ]; then
read -n $CONTENT_LENGTH POST_DATA <&0
while read line
do eval "echo ${line}"
done
fi
#since ash does not handle arrays we create variables using eval
IFS='&'
for PAIR in $POST_DATA
do
K=$(echo $PAIR | cut -f1 -d=)
VA=$(echo $PAIR | cut -f2 -d=)
VB=\"${VA//%3A/:}\"
#echo "<div>$K=$VB</div>"
eval POST_$K=\"$VB\"
done
#switch back to going through the config file
output="$hack_ini.new"
#name our output file
while IFS= read -r \ARGUMENT; do
#cycle through each line of the current config
#copy through all comments
if [ -z "$ARGUMENT" ]; then
echo -ne "\n" >> $output
elif [[ ${ARGUMENT:0:1} == "#" ]] ; then
#echo $ARGUMENT $'\n'
echo -ne $ARGUMENT"\n" >> $output
else
#for non-comments check to see if we have an entry in the POST data by deciphering the key from the ini file and using eval for our fake array
KEY=$(echo $ARGUMENT | cut -f1 -d=)
test=$(eval echo \$POST_$KEY)
#echo "key was $KEY test was ... $test <br /> "
if [[ "$test" ]]; then
#if in the fake array then we use the new value
#echo "<div style=\"color:#c00\">matched </div>"
echo -ne $KEY=\"$test\""\n" >> $output
else
#if not in the fake array we use the current value
#echo "<div>key not found</div>"
echo -ne $ARGUMENT"\n" >> $output
fi
fi
done < $hack_ini
shft $hack_ini
mv $output $hack_ini
updated=true
fi
function documentation_to_html
{
if [[ -f "$www_dir$1.md" ]]; then
printf '<div class="ii_explain"><pre>'
cat "$web_dir$1.md"
printf '</pre></div>'
fi
}
function ini_to_html_free
{
classes=""
if [ "$1" = "USB_DIRECT_MAC_ADDR" ]; then
classes=" mac_addr"
fi
if grep -q -wi "$1" numerics.txt; then
classes=" numeric"
fi
printf '<div class="ii"><div class="ii_key_DIV">%s</div><div class="ii_value_DIV"><input class="ii_value'$classes'" type="text" name="%s" value="%s" /></div>' $1 $1 $2
documentation_to_html $1
printf '</div>'
}
function ini_to_html_tf
{
printf '<div class="ii"><div class="ii_key_DIV">%s</div>' $1
printf '<div class="ii_value_DIV">'
if [[ "$2" == "true" ]]; then
printf '<input class="ii_radio" type="radio" name="%s" value="true" checked="checked" /> True &nbsp;' $1
printf '<input class="ii_radio" type="radio" name="%s" value="false" /> False &nbsp;' $1
else
printf '<input class="ii_radio" type="radio" name="%s" value="true" /> True &nbsp;' $1
printf '<input class="ii_radio" type="radio" name="%s" value="false" checked="checked" /> False &nbsp;' $1
fi
printf '</div>'
documentation_to_html $1
printf '</div>'
}
#function to handle camera feed
function html_cam_feed
{
printf '<img id="current_feed" src="/cgi-bin/jpeg.cgi?channel=1" class="feed" />'
}
function handle_css
{
echo -ne "<style type=\"text/css\">"
cat config.css
echo -ne '</style>';
}
echo -ne "<html><head><title>$title</title>"
handle_css wz_mini_web.css
echo '<script type="text/javascript" src="/config.js" ></script>'
echo -ne "</head>"
echo -ne '<body>'
echo -ne "<h1>$title</h1>";
if [ "$updated" = true ];
then
echo '<div class="message_DIV">configuration file updated. <a href="?action=reboot">Reboot<a/> to use changes. Or <a href="#revert">Revert</a> to a prior configuration</div>';
fi
html_cam_feed
if [ $base_hack_ini != $hack_ini ]; then
echo '<div><a href="?action=revert&version='$GET_version'">Revert</a> to this version</a></div>'
fi
echo -ne '<form name="update_config" method="POST" enctype="application/x-www-form-urlencoded" >'
CONFIG_BLOCK=0
while IFS= read -r ARGUMENT; do
if [ -z "$ARGUMENT" ] ; then
echo -ne ""
elif [[ ${ARGUMENT:0:1} == "#" ]] ; then
if [[ ${ARGUMENT:0:4} == "####" ]]; then
if [ "$CONFIG_BLOCK" -gt 0 ]; then
echo '</div>'
fi
CONFIG_BLOCK=$((CONFIG_BLOCK + 1))
echo '<div class="ii_block" block_number="'$CONFIG_BLOCK'" >'
BTITLE=${ARGUMENT//#/ }
echo -ne '<div class="ii_block_name">'$BTITLE'</div>'
else
echo -ne '<div class="ii_info">'$ARGUMENT'</div>'
fi
else
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VAL=$(echo $ARGUMENT | cut -f2 -d=)
VALUE=${VAL//\"/}
case "$VALUE" in
"true") ini_to_html_tf $KEY $VALUE ;;
"false") ini_to_html_tf $KEY $VALUE ;;
*) ini_to_html_free $KEY $VALUE
esac
fi
done < $hack_ini
if [ "$CONFIG_BLOCK" -gt 0 ]; then
echo '</div>'
fi
echo -ne '<input type="submit" name="update" value="Update" />'
echo -ne '</form>'
revert_menu $base_hack_ini $hack_ini
version_info "display_BAR"
echo -ne '</body></html>'

View File

@@ -0,0 +1,145 @@
BODY {
--feed_ratio: 2.5;
--display_bar_height: 20px;
--border-color: #ccc;
padding-bottom: var(--display_bar_height);
}
H1 {
background-color:blue;
color:white;
height:calc(108px * (var(--feed_ratio) - 1));
margin-top:0;
padding:5px;
margin-bottom:108px;
width:calc(100% - (192px * var(--feed_ratio)));
}
.ii_block {
border:3px solid var(--border-color);
margin-bottom:5px;
padding:2px;
border-radius:3px;
}
.ii_block_name {
background-color:var(--border-color);
text-align:center;
font-weight:bold;
color:#fff;
margin:-2px;
margin-bottom:3px;
}
.ii_info {
color:#333;
}
.ii {
width:100%;
position:relative;
display:flex;
}
.ii_key_DIV {
flex:0 0 40vw;
font-weight:bold;
}
.ii_value_DIV {
flex: 0 0 40vw;
text-align:right;
}
.ii_value {
width:75%;
}
.feed {
position:absolute;
top:10px;
right:0px;
width:calc(192px * var(--feed_ratio));
height:calc(108px * var(--feed_ratio));
z-index:10;
}
.ii_explain {
height:25px;
overflow:hidden;
width:25px;
position:relative;
}
.ii_explain:hover {
height: auto;
position: absolute;
left: 10%;
width: 80%;
border-radius: 3px;
padding: 5px;
background-color: #ccc;
z-index: 10;
border: 3px solid blue;
font-family: monospace;
white-space:pre-wrap;
}
.ii_explain:hover PRE {
white-space:pre-wrap;
}
.ii_explain:hover:after {
content:"";
border:none;
margin:0;
padding:0;
width:0;
height:0;
}
.ii_explain:after {
color:white;
content:"?";
text-align:center;
background-color:blue;
border:2px solid #ccc;
border-radius:3px;
padding-left:2px;
padding-right:2px;
position:absolute;
left:0;
top:0;
width:100%;
height:100%;
}
.revert_DIV {
display:flex;
width:100%;
}
.revert_DIV DIV:nth-child(1) {
width:30px;
}
.current_revert {
background-color:#edf054;
}
#display_BAR {
position:fixed;
bottom:0;
background-color:blue;
border-top:1px solid lightblue;
color:#fff;
display:flex;
width:100%;
height:var(--display_bar_height);
}
#display_BAR DIV {
padding-right:15px;
}
.ver_DIV {
padding-left:60px;
min-width:120px;
font-family:monospace;
flex: 1 0 120px;
}
.ver_DIV:before {
content:attr(vertype);
font-weight:bold;
padding-right:10px;
}
.ver_DIV[vertype="wz_mini"] {
flex:1 0 400px;
}
.fail_val {
background-color:#fcc;
}

View File

@@ -0,0 +1,29 @@
#!/bin/sh
#test for jpeg
if [[ $REQUEST_METHOD = 'GET' ]]; then
echo "entered GET"
#since ash does not handle arrays we create variables using eval
IFS='&'
for PAIR in $QUERY_STRING
do
K=$(echo $PAIR | cut -f1 -d=)
VA=$(echo $PAIR | cut -f2 -d=)
#VB=${VA//%3A/:}
#echo "<div>$K=$VA</div>"
eval GET_$K=$VA
done
fi
if [ -z "$GET_channel" ];
then
echo "X-Channel-Override: 0"
GET_channel=0
fi
echo "X-Channel: $GET_channel"
cmd jpeg "$GET_channel"

View File

@@ -0,0 +1,8 @@
WEB_CAM_BIT_RATE
WEB_CAM_FPS_RATE
BONDING_LINK_MONITORING_FREQ_MS
BONDING_DOWN_DELAY_MS
BONDING_UP_DELAY_MS
RTSP_PORT
AUDIO_PROMPT_VOLUME

View File

@@ -0,0 +1,59 @@
var feed_interval_frequency = 1000;
var mac_re = /^[0-9a-f]{1,2}([\.:-])(?:[0-9a-f]{1,2}\1){4}[0-9a-f]{1,2}$/mi;
// https://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
function isInt(value) {
return !isNaN(value) &&
parseInt(Number(value)) == value &&
!isNaN(parseInt(value, 10));
}
function scrollTop()
{
window.scrollTo({
top: 0,
behavior: "smooth"
});
}
window.onload = function()
{
var feed = document.getElementById("current_feed");
function update_image()
{
feed.src = feed.src.split("&")[0] + "&load=" + new Date().getTime();
}
feed_interval = setInterval(update_image, feed_interval_frequency);
document.querySelector('[name="update_config"]').addEventListener('submit',
function(e){
const mac_addrs = document.getElementsByClassName('mac_addr');
for (let i=0; i < mac_addrs.length; i++) {
mac_addrs[i].classList.remove("fail_val");
if (mac_addrs[i].value == "") { continue; }
if (!mac_re.test(mac_addrs[i].value)) {
mac_addrs[i].classList.add("fail_val");
scrollTop();
console.log("failed on mac address test for " + mac_addrs[i].name + " for value " + mac_addrs[i].value);
e.preventDefault();
}
}
const numerics = document.getElementsByClassName('numeric');
for (let i=0; i < numerics.length; i++) {
numerics[i].classList.remove("fail_val");
if (numerics[i].value == "") { continue; }
if (!isInt(numerics[i].value)) {
numerics[i].classList.add("fail_val");
scrollTop();
console.log("failed on integer test for " + numerics[i].name);
e.preventDefault();
}
}
}
);
}

View File

@@ -0,0 +1,10 @@
<html>
<head><title>WZ Mini</title></head>
<body>
<h1>wz_mini web server</h1>
<p><a href="cgi-bin/config.cgi">Configuration Editor</a></p>
<p><a href="car/car.html">Car Interface</a></p>
<p><a href="cgi-bin/jpeg.cgi">Current Screen</a></p>
</body>
</html>

View File

@@ -79,6 +79,7 @@ NIGHT_DROP_DISABLE="false"
DISABLE_MOTOR="false"
ENABLE_FSCK_ON_BOOT="false"
ENABLE_CAR_DRIVER="false"
ENABLE_LOCAL_DNS="false"
#####DEBUG#####
#drops you to a shell via serial, doesn't load app_init.sh
@@ -86,5 +87,8 @@ DEBUG_ENABLED="false"
DEBUG_INITRAMFS_ENABLED="false"
DEBUG_PASSWORD="false"
####WEB####
WEB_SERVER_ENABLED="false"
#####SCRIPTING#####
CUSTOM_SCRIPT_PATH="/opt/wz_mini/etc/crontab/start_cron.sh"