I try to do an update firmeware via Git repo from an ESP8266. But I don't know how. The repo is private, that mean I need a password, I read that I can use HTTPclient library for authentication. How Github's authentication works?
Also, do I need some extra code for Update library? HTTPclient supports HTTPS?
EDIT: Here some example of my code, but is for a public repo:
update.cpp (I have it in a separate header file)
//#define repo "https://github.com/username/reponame/branch/path/to/file?raw=true"
#define repo "https://raw.githubusercontent.com/username/reponame/branch/path/to/file"
t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);
// Or:
//t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 80, "file.bin");
I have configured httpUpdate error message, it show the next error:
CALLBACK: HTTP update fatal error code -5
HTTP_UPDATE_FAILD Error (-5): HTTP error: connection lost
There is a different way to do an update from GitHub, first GitHub use a HTTPS connection, that mean you need configure before a TLS/SSL setting, Also, port 80 is for insecure connections, 443 is designed for secure connections.
Public repository (insecure)
This is the easier way, just add .setInsecure() to the wifi client from WiFiClientSecure.h library, this allow you to stablish a connection ignoring all alerts from http connection.
WiFiClientSecure client;
client.setInsecure();
Is insecure, only do this for testing, not for production.
You must use https://raw.githubusercontent.com, this is for download raw data from GitHub's public repos, just the file. Your full link to the file must be:
#define repo "https://raw.githubusercontent.com/<user>/<repo>/master/<path to the .bin>"
t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);
Replace <user> with your user name, and <repo> with your repository's name. <path to the .bin> is something like "folder/folder/firmware.bin"
Public repository (secure):
There is an example in the official GitHub repository of ESP8266/Arduino.
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino
You can follow this example for a secure connection with httpUpdate. Also you will need to download the certs, this can do by executing the next script in the same folder of your project:
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py
If you are using windows, to run this script, you will need add "OpenSSL.exe" to the path, Git comes with it, you add the Git's bin folder to the path. Also, you will need one more file "ar.exe", this come with the ESP8266 core. You can also put this two .exe files in the same folder of the script.
For Arduino IDE is something like:
%userprofile%\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\2.5.0-4-b40a506\xtensa-lx106-elf\bin
For PlaformIO is:
%userprofile%\.platformio\packages\toolchain-xtensa\xtensa-lx106-elf\bin
When the script finish, this will create a folder named data with certs.ar inside. Upload this file system image to ESP8266 with LittleFS.
Private repository:
This is the same as the previous one, just couple of things to change, and we will make change to the ESP8266httpUpdate library. We use the same example to httpupdatesecure and you will need to configure a token in your GitHub account.
Follow the instructions of the help page of GitHub for create a token:
https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
You only need this option selected
Copy the token and save it, you only able see one time.
You can't use raw.githubusercontent.com, will give you error 404, this only work for public repos. You need: api.github.com. You full link looks like:
https://api.github.com/repos/<user>/<repo>/contents/<path to the .bin>
And you need to add the headers to the http request, in the ESP8266httpUpdate.cpp, you must put it in the function HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate in the part where start add headers:
http.addHeader(F("Accept"), "application/vnd.github.v3.raw");
http.addHeader(F("authorization"), "Bearer <your token>");
Replace <your token> with the one that you created and saved before.
Remember, edit this library will affect all your future projects, so, when you are done revert or comment the two headers that you added to the library.
NOTE: Using a token will allow your application access to all your
repos, even if your token is read-only. Currently, is not possible generate a token for a specific repo on GitHub. Be careful if you share your
code. Better use a dummy account with only one repo for this.
[Edit] - It's wokring now, it was an arduino core bug, installed 2.7.4 and now it works(come form 3.0.2-dev)
Doesn't work here "Verify Bin Header Failed", tryied hosting bin on github, 000webhosting, surge, not seens anything related to web server problem :(
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
//#include <WiFiClientSecure.h>
#include <CertStoreBearSSL.h>
BearSSL::CertStore certStore;
#include <time.h>
#include <FS.h>
#include <LittleFS.h>
const String FirmwareVer={"1"};
//#define URL_fw_Version "/teste/key/version.txt"
//#define URL_fw_Bin "https://pacauiot.surge.sh/teste/key/firmware.bin"
//const char* host = "pacauiot.surge.sh";
#define URL_fw_Bin "https://fourieristic-prison.000webhostapp.com/meucu.php"
const char* host = "fourieristic-prison.000webhostapp.com";
const int httpsPort = 443;
const char* ssid = "wifi";
const char* password = "wifipass";
#define RTC_UTC_TEST 1510592825
void setClock()
// *******************************************************************************************
{
// see https://github.com/esp8266/Arduino/issues/4637
time_t now;
now = time(nullptr); // if there's no time, this will have a value of 28800; Thu Jan 1 08:00:00 1970
Serial.print("Initial time:"); Serial.println(now);
Serial.println(ctime(&now));
int myTimezone = -7;
int dst = 0;
int SecondsPerHour = 3600;
int MAX_TIME_RETRY = 60;
int i = 0;
// it is unlikely that the time is already set since we have no battery;
// if no time is avalable, then try to set time from the network
if (now <= 1500000000) {
// try to set network time via ntp packets
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // see https://github.com/esp8266/Arduino/issues/4749#issuecomment-390822737
// Starting in 2007, most of the United States and Canada observe DST from
// the second Sunday in March to the first Sunday in November.
// example setting Pacific Time:
setenv("TZ", "EST4EDT", 1); // see https://users.pja.edu.pl/~jms/qnx/help/watcom/clibref/global_data.html
// | month 3, second sunday at 2:00AM
// | Month 11 - firsst Sunday, at 2:00am
// Mm.n.d
// The dth day(0 <= d <= 6) of week n of month m of the year(1 <= n <= 5, 1 <= m <= 12, where
// week 5 means "the last d day in month m", which may occur in the fourth or fifth week).
// Week 1 is the first week in which the dth day occurs.Day zero is Sunday.
tzset();
Serial.print("Waiting for time(nullptr).");
i = 0;
while (!time(nullptr)) {
Serial.print(".");
delay(1000);
i++;
if (i > MAX_TIME_RETRY) {
Serial.println("Gave up waiting for time(nullptr) to have a valid value.");
break;
}
}
}
Serial.println("");
// wait and determine if we have a valid time from the network.
now = time(nullptr);
i = 0;
Serial.print("Waiting for network time.");
while (now <= 1500000000) {
Serial.print(".");
delay(1000); // allow a few seconds to connect to network time.
i++;
now = time(nullptr);
if (i > MAX_TIME_RETRY) {
Serial.println("Gave up waiting for network time(nullptr) to have a valid value.");
break;
}
}
Serial.println("ok");
// get the time from the system
char *tzvalue;
tzvalue = getenv("TZ");
Serial.print("Network time:"); Serial.println(now);
Serial.println(ctime(&now));
Serial.print("tzvalue for timezone = "); Serial.println(tzvalue);
// TODO - implement a web service that returns current epoch time to use when NTP unavailable (insecure SSL due to cert date validation)
// some networks may not allow ntp protocol (e.g. guest networks) so we may need to fudge the time
if (now <= 1500000000) {
Serial.println("Unable to get network time. Setting to fixed value. \n");
// set to RTC text value
// see https://www.systutorials.com/docs/linux/man/2-settimeofday/
//
//struct timeval {
// time_t tv_sec; /* seconds */
// suseconds_t tv_usec; /* microseconds */
//};
timeval tv = { RTC_UTC_TEST, 0 };
//
//struct timezone {
// int tz_minuteswest; /* minutes west of Greenwich */
// int tz_dsttime; /* type of DST correction */
//};
timezone tz = { myTimezone * 60 , 0 };
// int settimeofday(const struct timeval *tv, const struct timezone *tz);
settimeofday(&tv, &tz);
}
now = time(nullptr);
Serial.println("Final time:"); Serial.println(now);
Serial.println(ctime(&now));
}
void FirmwareUpdate()
{
//WiFiClientSecure client;
BearSSL::WiFiClientSecure client;
bool mfln = client.probeMaxFragmentLength(host, 443, 1024); // server must be the same as in ESPhttpUpdate.update()
Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
if (mfln) {
client.setBufferSizes(1024, 1024);
}
client.setCertStore(&certStore);
//client.setTrustAnchors(&cert);
// if (!client.connect(host, httpsPort)) {
// Serial.println("Connection failed");
// return;
// }
// client.print(String("GET ") + URL_fw_Version + " HTTP/1.1\r\n" +
// "Host: " + host + "\r\n" +
// "User-Agent: BuildFailureDetectorESP8266\r\n" +
// "Connection: close\r\n\r\n");
// while (client.connected()) {
// String line = client.readStringUntil('\n');
// if (line == "\r") {
// //Serial.println("Headers received");
// break;
// }
// }
// String payload = client.readStringUntil('\n');
//
// payload.trim();
// if(payload.equals(FirmwareVer) )
// {
// Serial.println("Device already on latest firmware version");
// }
if(1==2){
}
else
{
Serial.println("New firmware detected");
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, URL_fw_Bin);
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
}
void connect_wifi();
unsigned long previousMillis_2 = 0;
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 10000;
const long mini_interval = 5000;
void repeatedCall(){
unsigned long currentMillis = millis();
if ((currentMillis - previousMillis) >= interval)
{
// save the last time you blinked the LED
previousMillis = currentMillis;
setClock();
FirmwareUpdate();
}
if ((currentMillis - previousMillis_2) >= mini_interval) {
static int idle_counter=0;
previousMillis_2 = currentMillis;
Serial.print(" Active fw version:");
Serial.println(FirmwareVer);
Serial.print("Idle Loop(5s)...");
//Serial.println(idle_counter++);
if(idle_counter%2==0)
digitalWrite(LED_BUILTIN, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
if(WiFi.status() == !WL_CONNECTED)
connect_wifi();
}
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
Serial.println("Start Xuxu");
WiFi.mode(WIFI_STA);
connect_wifi();
setClock();
pinMode(LED_BUILTIN, OUTPUT);
LittleFS.begin();
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
Serial.print(F("Number of CA certs read: "));
Serial.println(numCerts);
if (numCerts == 0) {
Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
return; // Can't connect to anything w/o certs!
}
//repeatedCall();
FirmwareUpdate();
}
void loop()
{
}
void connect_wifi()
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("O");
}
Serial.println("Connected to WiFi");
}
Related
Hope all is going well.
I'm trying to ping STM32H743ZI NUCLEO 144 using LWIP middle-ware. Code generated by CubeMX.
Configurations:
Set the HCLK to 400 MHz
Enabled the CPU ICache and DCache (under Cortex_M7 Configuration)
Enabled MPU (Region0, Region1 & Region2)
Enabled LWIP
Selected LAN8742 as the Driver_PHY (under LwIP>Platform Settings)
DHCP disabled (IP, MASK: 255,255,255,000 , Gateway: Modem IP)
RTOS disabled
LWIP_HTTPD, LWIP_HTTPD_CGI enabled
LWIP_HTTPD_SSI enabled
LWIP_HTTPD_MAX_TAG_NAME_LEN set to 16
ICMP enabled (LWIP_BROADCAST_PING and LWIP_MULTICAST_PING in LwIP Key Options>IPMP Options).
Code Generated for Keil V5
MX_LWIP_Process added to the main function in While loop.
while(1)
{
MX_LWIP_Process();
}
I don't know how should I configure the CubeMX or change the generated code to be able to ping my board.
My_File
this will probably help you (it did for me):
Information about this issue can be found here.
https://community.st.com/s/article/FAQ-Ethernet-not-working-on-STM32H7x3
Memory buffers need to be assigned to RAM that can be accessed by the Ethernet
peripheral.
You may need to adjust the tour stack/heap size.
The default Ethernet GPIOs speed may be too low.
You may need to configure the MPU.
You may likely need to change your linker script.
On this page you will find good information:
https://github.com/MX-Master/STM32H7_Nucleo-H743ZI_Ethernet_LwIP
The HAL_Delay mentioned may not be required, though.
In the file lan8742.c (driver), I added an extra line for the LAN8742_Init function, around line 190, to set auto-negotiation:
// Link did not come up after HW reset.
pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_AUTONEGO_EN);
So that function looks like:
// Used in ethernetif.c, 363, static void low_level_init(struct netif *netif)
int32_t LAN8742_Init(lan8742_Object_t *pObj)
{
uint32_t tickstart = 0, regvalue = 0, addr = 0;
int32_t status = LAN8742_STATUS_OK;
if(pObj->Is_Initialized == 0)
{
if(pObj->IO.Init != 0)
{
/* GPIO and Clocks initialization */
pObj->IO.Init();
}
/* for later check */
pObj->DevAddr = LAN8742_MAX_DEV_ADDR + 1;
/* Get the device address from special mode register */
for(addr = 0; addr <= LAN8742_MAX_DEV_ADDR; addr ++)
{
if(pObj->IO.ReadReg(addr, LAN8742_SMR, ®value) < 0)
{
status = LAN8742_STATUS_READ_ERROR;
/* Can't read from this device address
continue with next address */
continue;
}
if((regvalue & LAN8742_SMR_PHY_ADDR) == addr)
{
pObj->DevAddr = addr;
status = LAN8742_STATUS_OK;
break;
}
}
if(pObj->DevAddr > LAN8742_MAX_DEV_ADDR)
{
status = LAN8742_STATUS_ADDRESS_ERROR;
}
/* if device address is matched */
if(status == LAN8742_STATUS_OK)
{
/* set a software reset */
if(pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_SOFT_RESET) >= 0)
{
/* get software reset status */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) >= 0)
{
tickstart = pObj->IO.GetTick();
/* wait until software reset is done or timeout occurred */
while(regvalue & LAN8742_BCR_SOFT_RESET)
{
if((pObj->IO.GetTick() - tickstart) <= LAN8742_SW_RESET_TO)
{
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) < 0)
{
status = LAN8742_STATUS_READ_ERROR;
break;
}
}
else
{
status = LAN8742_STATUS_RESET_TIMEOUT;
}
}
}
else
{
status = LAN8742_STATUS_READ_ERROR;
}
}
else
{
status = LAN8742_STATUS_WRITE_ERROR;
}
}
}
// Jack 2019-03-25, Link did not come up after HW reset.
pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_AUTONEGO_EN);
if(status == LAN8742_STATUS_OK)
{
tickstart = pObj->IO.GetTick();
/* Wait for 2s to perform initialization */
while((pObj->IO.GetTick() - tickstart) <= LAN8742_INIT_TO)
{
}
pObj->Is_Initialized = 1;
}
return status;
}
I apologize if this doesn't make sense since I'm still a newbie with using a raspberry pi and this is my first time posting on StackOverflow.
I am making a web app that lets me stream video to and from a raspberry pi while also letting me send keycodes. The sent keycodes would ultimately let me control servos on a drone. After scouring the internet, I figured that the simplest way to stream 2-way video is by using uv4l so I have it installed along with uv4l-webrtc on my raspberry pi. I hooked up some GPIO pins to a flight controller and I am using pigpio to send PWM signals to it, which I then monitor using CleanFlight.
Right now, I can manipulate with keypresses the roll, pitch, etc. of the flight controller using a python script if I access the pi remotely using VNC, but I would like to ultimately be able to do this through my custom web page that is being served by the uv4l-server. I am trying to use the WebRTC Data Channels, but I'm having some trouble understanding what I would need to do to recognize the messages sent through the data channels. I know that the data channels are opened when a video call is initiated and I've tried the test in this link to see if I can indeed send keycodes to the pi (and I can).
My problem right now is that I have no idea where those sent messages go or how I can get them so I can incorporate them into my python script. Would I need to make a server that would listen for the keycodes being sent to the pi?
tl;dr I have a python script on a raspberry pi to control servos on a flight controller using keypresses and a separate webpage that streams video using WebRTC, but I have no idea how to combine them together using WebRTC data channels.
Thanks to #adminkiam for the solution. Here's a version of the python script that now listens to the socket. It's essentially a variation of this code by the person who made pigpio:
import socket
import time
import pigpio
socket_path = '/tmp/uv4l.socket'
try:
os.unlink(socket_path)
except OSError:
if os.path.exists(socket_path):
raise
s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
ROLL_PIN = 13
PITCH_PIN = 14
YAW_PIN = 15
MIN_PW = 1000
MID_PW = 1500
MAX_PW = 2000
NONE = 0
LEFT_ARROW = 1
RIGHT_ARROW = 2
UP_ARROW = 3
DOWN_ARROW = 4
LESS_BTN = 5
GREATER_BTN = 6
print 'socket_path: %s' % socket_path
s.bind(socket_path)
s.listen(1)
def getch(keyCode):
key = NONE
if keyCode == 188:
key = LESS_BTN
elif keyCode == 190:
key = GREATER_BTN
elif keyCode == 37:
key = LEFT_ARROW
elif keyCode == 39:
key = RIGHT_ARROW
elif keyCode == 38:
key = UP_ARROW
elif keyCode == 40:
key = DOWN_ARROW
return key
def cleanup():
pi.set_servo_pulsewidth(ROLL_PIN, 0)
pi.set_servo_pulsewidth(PITCH_PIN, 0)
pi.set_servo_pulsewidth(YAW_PIN, 0)
pi.stop()
while True:
print 'awaiting connection...'
connection, client_address = s.accept()
print 'client_address %s' % client_address
try:
print 'established connection with', client_address
pi = pigpio.pi()
rollPulsewidth = MID_PW
pitchPulsewidth = MID_PW
yawPulsewidth = MID_PW
pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)
while True:
data = connection.recv(16)
print 'received message"%s"' % data
time.sleep(0.01)
key = getch(int(data))
rollPW = rollPulsewidth
pitchPW = pitchPulsewidth
yawPW = yawPulsewidth
if key == UP_ARROW:
pitchPW = pitchPW + 10
if pitchPW > MAX_PW:
pitchPW = MAX_PW
elif key == DOWN_ARROW:
pitchPW = pitchPW - 10
if pitchPW < MIN_PW:
pitchPW = MIN_PW
elif key == LEFT_ARROW:
rollPW = rollPW - 10
if rollPW < MIN_PW:
rollPW = MIN_PW
elif key == RIGHT_ARROW:
rollPW = rollPW + 10
if rollPW > MAX_PW:
rollPW = MAX_PW
elif key == GREATER_BTN:
yawPW = yawPW + 10
if yawPW > MAX_PW:
yawPW = MAX_PW
elif key == LESS_BTN:
yawPW = yawPW - 10
if yawPW < MIN_PW:
yawPW = MIN_PW
if rollPW != rollPulsewidth:
rollPulsewidth = rollPW
pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
if pitchPW != pitchPulsewidth:
pitchPulsewidth = pitchPW
pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
if yawPW != yawPulsewidth:
yawPulsewidth = yawPW
pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)
if data:
print 'echo data to client'
connection.sendall(data)
else:
print 'no more data from', client_address
break
finally:
# Clean up the connection
cleanup()
connection.close()
When a WebRTC data channel is created between UV4L and the other WebRTC peer (i.e. a browser, Janus Gateway, etc...), UV4L creates a full-duplex Unix Domain Socket (/tmp/uv4l.socket by default) from/to which you can receive/send messages on the Raspberry Pi. Your python script should just open, listen and read to the socket for the incoming messages from the e.g. web application and/or write the messages to the same socket for the web app to receive them. An example doing this in C++ is under the link to the tutorial you pointed out in your question:
/*
Copyright (c) 2016 info#linux-projects.org
All rights reserved.
Redistribution and use in source and binary forms are permitted
provided that the above copyright notice and this paragraph are
duplicated in all such forms and that any documentation,
advertising materials, and other materials related to such
distribution and use acknowledge that the software was developed
by the linux-projects.org. The name of the
linux-projects.org may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* This is a simple echo server.
* It creates to a unix domain socket of type SOCK_SEQPACKET specified by
* command line, listens to it waiting for incoming messages from clients
* (e.g. UV4L) and replies the received messages back to the senders.
*
* Example:
* $ ./datachannel_server /tmp/uv4l.socket
*
* To compile this program you need boost v1.60 or greater, for example:
* g++ -Wall -I/path/to/boost/include/ -std=c++11 datachannel_server.cpp -L/path/to/boost/lib -l:libboost_coroutine.a -l:libboost_context.a -l:libboost_system.a -l:libboost_thread.a -pthread -o datachannel_server
*/
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <cstdio>
#include <array>
#include <functional>
#include <iostream>
#if !defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
#error Local sockets not available on this platform.
#endif
constexpr std::size_t MAX_PACKET_SIZE = 1024 * 16;
namespace seqpacket {
struct seqpacket_protocol {
int type() const {
return SOCK_SEQPACKET;
}
int protocol() const {
return 0;
}
int family() const {
return AF_UNIX;
}
using endpoint = boost::asio::local::basic_endpoint<seqpacket_protocol>;
using socket = boost::asio::generic::seq_packet_protocol::socket;
using acceptor = boost::asio::basic_socket_acceptor<seqpacket_protocol>;
#if !defined(BOOST_ASIO_NO_IOSTREAM)
/// The UNIX domain iostream type.
using iostream = boost::asio::basic_socket_iostream<seqpacket_protocol>;
#endif
};
}
using seqpacket::seqpacket_protocol;
struct session : public std::enable_shared_from_this<session> {
explicit session(seqpacket_protocol::socket socket) : socket_(std::move(socket)) {}
~session() {
//std::cerr << "session closed\n";
}
void echo(boost::asio::yield_context yield) {
auto self = shared_from_this();
try {
for (;;) {
seqpacket_protocol::socket::message_flags in_flags = MSG_WAITALL, out_flags = MSG_WAITALL;
// Wait for the message from the client
auto bytes_transferred = socket_.async_receive(boost::asio::buffer(data_), in_flags, yield);
// Write the same message back to the client
socket_.async_send(boost::asio::buffer(data_, bytes_transferred), out_flags, yield);
}
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
socket_.close();
}
}
void go() {
boost::asio::spawn(socket_.get_io_service(), std::bind(&session::echo, this, std::placeholders::_1));
}
private:
seqpacket_protocol::socket socket_;
std::array<char, MAX_PACKET_SIZE> data_;
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: datachannel_server <file> (e.g. /tmp/uv4l.socket)\n";
std::cerr << "*** WARNING: existing file is removed ***\n";
return EXIT_FAILURE;
}
boost::asio::io_service io_service;
std::remove(argv[1]);
boost::asio::spawn(io_service, [&](boost::asio::yield_context yield) {
seqpacket_protocol::acceptor acceptor_(io_service, seqpacket_protocol::endpoint(argv[1]));
for (;;) {
boost::system::error_code ec;
seqpacket_protocol::socket socket_(io_service);
acceptor_.async_accept(socket_, yield[ec]);
if (!ec)
std::make_shared<session>(std::move(socket_))->go();
}
});
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return EXIT_FAILURE;
}
}
I am attempting to send data from device to host using the Gazelle protocol, however, when reading a time varying signal in on MATLAB the values continuously change elements in the array.
Here is the Simblee/Rfduino host code:
#include <SimbleeGZLL.h>
device_t role = HOST;
char array[5];
void setup() {
Serial.begin(9600);
SimbleeGZLL.begin(role);
timer_one(1); // 1 ms timer
}
void loop() {
Serial.flush();
printf(EMG);
}
void SimbleeGZLL_onReceive(device_t device, int rssi, char *data, int len)
{
if (len > 0) {
digitalWrite(2,HIGH);
array[0] = data[0];
array[1] = data[1];
array[2] = data[2];
array[3] = data[3];
array[4] = '\0';
} else SimbleeGZLL.sendToDevice(device, 'A');
}
And the device code:
include
device_t role = DEVICE1;
volatile int state;
char array[4];
void setup() {
SimbleeGZLL.begin(role);
Serial.begin(9600);
timer_one(1);
}
void loop() {
array[0] = analogRead(2);
array[1] = analogRead(3);
array[2] = analogRead(4);
array[3] = analogRead(5);
SimbleeGZLL.sendToHost(EMG,4);
}
Could someone please provide some assistance to identify where the issue may lie?
Thank you!
Matlab is not super reliable with serial communication. I actually had a similar issue with a serial device where the input values would be out of order. Are you signaling when to start and stop printing? What does your matlab code look like?
I would set up a ring buffer on the host and the device to deal with the asycn time issues.
You are going to get timing issues with the current method. What kind of frequency are you going for? The analogRead is super slow, and double multiple in a row seems to make things even slower. Could you try to set up an ADC interrupt?
Where is your timer code?
I have a sketch to take information (Lat, Long) from an EM-406a GPS receiver and write the information to an SD card on an Arduino shield.
The program is as follows:
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SD.h>
TinyGPSPlus gps;
SoftwareSerial ss(4, 3); //pins for the GPS
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
void setup()
{
Serial.begin(115200); //for the serial output
ss.begin(4800); //start ss at 4800 baud
Serial.println("gpsLogger by Aaron McRuer");
Serial.println("based on code by Mikal Hart");
Serial.println();
//initialize the SD card
if(!card.init(SPI_FULL_SPEED, 9))
{
Serial.println("card.init failed");
}
//initialize a FAT volume
if(!volume.init(&card)){
Serial.println("volume.init failed");
}
//open the root directory
if(!root.openRoot(&volume)){
Serial.println("openRoot failed");
}
//create new file
char name[] = "WRITE00.TXT";
for (uint8_t i = 0; i < 100; i++){
name[5] = i/10 + '0';
name[6] = i%10 + '0';
if(file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)){
break;
}
}
if(!file.isOpen())
{
Serial.println("file.create");
}
file.print("Ready...\n");
}
void loop()
{
bool newData = false;
//For one second we parse GPS data and report some key values
for (unsigned long start = millis(); millis() - start < 1000;)
{
while (ss.available())
{
char c = ss.read();
//Serial.write(c); //uncomment this line if you want to see the GPS data flowing
if(gps.encode(c)) //did a new valid sentence come in?
newData = true;
}
}
if(newData)
{
file.write(gps.location.lat());
file.write("\n");
file.write(gps.location.lng());
file.write("\n");
}
file.close();
}
When I open up the file on the SD card when the program is finished executing, I get a message that it has an encoding error.
I'm currently inside (and unable to get a GPS signal, thus the 0), but the encoding problem needs to be tackled, and there should be as many lines as there are seconds that the device has been on. There's only that one. What do I need to do to make things work correctly here?
Closing the file in the loop, and never reopening it, is the reason there's only one set of data in your file.
Are you sure gps.location.lat() and gps.location.lng() return strings, not an integer or float? That would explain the binary data and the "encoding error" you see.
I am trying to erase one page in flash on an STM32F103RB like so:
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_OPTERR);
FLASHStatus = FLASH_ErasePage(Page);
However, FLASH_ErasePage fails producing FLASH_ERROR_WRP
Manually enabling/disabling write protection in the stm32-linker tool doesn't fix the problem.
Basically FLASH_ErasePage fails with WRP error without trying to do anything if there's previous WRP error in the status register.
What comes to your FLASH_ClearFlag call, at least FLASH_FLAG_BSY will cause assert_param(IS_FLASH_CLEAR_FLAG(FLASH_FLAG)); to fail (though I'm not really sure what happens in this case).
#define IS_FLASH_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFFC0FD) == 0x00000000) && ((FLAG) != 0x00000000))
What is your page address ? Which address are you trying to access ?
For instance, this example is tested on STM32F100C8 in terms of not only erasing but also writing data correctly.
http://www.ozturkibrahim.com/TR/eeprom-emulation-on-stm32/
If using the HAL driver, your code might look like this (cut'n paste from an real project)
static HAL_StatusTypeDef Erase_Main_Program ()
{
FLASH_EraseInitTypeDef ins;
uint32_t sectorerror;
ins.TypeErase = FLASH_TYPEERASE_SECTORS;
ins.Banks = FLASH_BANK_1; /* Do not care, used for mass-erase */
#warning We currently erase from sector 2 (only keep 64KB of flash for boot))
ins.Sector = FLASH_SECTOR_4;
ins.NbSectors = 4;
ins.VoltageRange = FLASH_VOLTAGE_RANGE_3; /* voltage-range defines how big blocks can be erased at the same time */
return HAL_FLASHEx_Erase (&ins, §orerror);
}
The internal function in the HAL driver that actually does the work
void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)
{
uint32_t tmp_psize = 0U;
/* Check the parameters */
assert_param(IS_FLASH_SECTOR(Sector));
assert_param(IS_VOLTAGERANGE(VoltageRange));
if(VoltageRange == FLASH_VOLTAGE_RANGE_1)
{
tmp_psize = FLASH_PSIZE_BYTE;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_2)
{
tmp_psize = FLASH_PSIZE_HALF_WORD;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_3)
{
tmp_psize = FLASH_PSIZE_WORD;
}
else
{
tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
}
/* If the previous operation is completed, proceed to erase the sector */
CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
FLASH->CR |= tmp_psize;
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);
FLASH->CR |= FLASH_CR_SER | (Sector << POSITION_VAL(FLASH_CR_SNB));
FLASH->CR |= FLASH_CR_STRT;
}
Second thing to check. Is interrupts enabled, and is there any hardware access between the unlock call and the erase call?
I hope this helps