I wanted to exchange some Informations from the Windows PC to an arduino Leonardo. I wanted to use that data and display it on a SPI TFT-Display. The code worked fine and suddenly the code stopped working. I'm not an edvanced Developer so I wished someone here could help me find the error in my code/s. The main Problem is that the Python code should repond to the words which were send from the arduino. I my case the python code should answer with ,,clock: 12:34" if the Arduino said ,,First Message from Arduino!".
The Arduino Code:
#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <RotaryEncoder.h>
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 12
#define PIN_IN1 9
#define PIN_IN2 11
bool LoadingScreenIsAcitv = true;
bool firstSerialMessage = true;
int loadingSteps = 0;
int clock;
int profil = 1;
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
String incomingByte;
String oldincomingByte;
String firstCommand;
void MainScreen(){
tft.fillScreen(ST7735_BLACK);
tft.fillRect(10,10,60,15,ST7735_BLACK);
tft.drawRoundRect(5,5,70,25,8,ST7735_WHITE);
tft.setCursor(5,117);
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(1);
tft.write("Profil: Desktop(Standard)");
tft.setCursor(5,50);
tft.write("1. Launch Badlion");
tft.setCursor(5,60);
tft.write("2. Launch Minecraft");
tft.setCursor(5,70);
tft.write("3. Launch Fusion360");
tft.setCursor(5,80);
tft.write("4. Launch Visual-Studio");
tft.setCursor(5,90);
tft.write("5. Empty Launch-Slot");
tft.setCursor(5,100);
tft.write("6. Empty Launch-Slot");
}
void Loading(){
tft.fillRoundRect(5,80,10,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
if(loadingSteps == 1){
tft.fillRoundRect(5,80,30,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,50,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,70,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,90,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,110,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,130,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
delay(15);
tft.fillRoundRect(5,80,150,25,5,ST7735_GREEN);
tft.drawRoundRect(5,80,150,25,5,ST7735_WHITE);
MainScreen();
}
}
void setup() {
tft.initR(INITR_BLACKTAB);
tft.setRotation(3);
tft.fillScreen(INITR_BLACKTAB);
tft.setCursor(23,25);
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(2);
tft.write("Loading...");
Loading();
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
static int pos = 0;
encoder.tick();
int newPos = encoder.getPosition();
if(pos != newPos){
tft.println(newPos);
pos = newPos;
}
if(firstSerialMessage == true){
if(Serial){
Serial.println("First Message from Arduino!");
firstSerialMessage = false;
}
}
if (Serial.available() > 0) {
incomingByte = Serial.readStringUntil('\n');
if (incomingByte == "end loading") {
if(LoadingScreenIsAcitv == true){
loadingSteps = 1;
Loading();
LoadingScreenIsAcitv = false;
//Serial.println("Main Screen was Loaded!");
MainScreen();
}
} else if(LoadingScreenIsAcitv == false){
String word1 = "clock";
String word2 = "date";
String word3 = "day";
if(strstr(incomingByte.c_str(),word1.c_str())){
char dummyy1[50];
String dummy1 = incomingByte;
Serial.write("Clock wurde empfangen!");
dummy1.remove(0,6);
dummy1.toCharArray(dummyy1,50);
tft.fillRect(10,10,60,15,ST7735_BLACK);
tft.setCursor(10,10);
tft.setTextSize(2);
tft.write(dummyy1);
} else if(strstr(incomingByte.c_str(),word2.c_str())){
digitalWrite(LED_BUILTIN,HIGH);
char dummyy2[50];
String dummy2 = incomingByte;
Serial.write("Datum wurde empfangen!");
dummy2.remove(0,5);
dummy2.toCharArray(dummyy2,50);
tft.setCursor(85,10);
tft.setTextSize(1);
tft.write(dummyy2);
} else if(strstr(incomingByte.c_str(),word3.c_str())){
digitalWrite(LED_BUILTIN,HIGH);
char dummyy3[50];
String dummy3 = incomingByte;
Serial.write("Tag wurde empfangen!");
dummy3.remove(0,4);
dummy3.toCharArray(dummyy3,50);
tft.setCursor(85,20);
tft.setTextSize(1);
tft.write(dummyy3);
} else {
Serial.write("Eingabe wurde auf dem TFT ausgegeben");
tft.print(incomingByte);
}
} else{
Serial.print("invalid input");
}
}
}
The python code:
import serial
import serial.tools.list_ports
import time
import datetime
import pandas as pd
from datetime import date
ports = list(serial.tools.list_ports.comports())
for p in ports:
serialcomm = serial.Serial(p.name, 9600)
serialcomm.timeout = 1
f = True
while True:
if f == True:
i = input("Enter Input: ").strip()
f = False
if i == "Close":
print('finished')
break
serialcomm.write(i.encode())
dummy = serialcomm.read_until('\n').decode('ascii')
print(dummy)
time.sleep(0.5)
dummy = serialcomm.read_until('\n').decode('ascii')
print(dummy)
now = datetime.datetime.now()
if(dummy == "First Message from Arduino!"):
addstringsclock = "clock:" + str(now.hour) + ":" + str(now.minute)
serialcomm.write(addstringsclock.encode())
print("Uhrzeit wurde übermittelt: " + addstringsclock)
if(dummy == "First Message from Arduino!"):
time.sleep(1)
addstringsdate = "date:" + str(now.day) + "." + str(now.month) + "." + str(now.year)
serialcomm.write(addstringsdate.encode())
print("Datum wurde übermittelt: " + addstringsdate)
if(dummy == "First Message from Arduino!"):
time.sleep(2)
temp = pd.Timestamp(date.today())
addstringsday = "day:" + temp.day_name()
serialcomm.write(addstringsday.encode())
print("Tag wurde übermittelt: " + addstringsday)
serialcomm.close()
I tried troubleshooting and my thought is that the error is somewhere in the python code. I tried to run it from the python IDLE.
Related
I have a piece of code in python. It is related to client Socket programming. I want to get the NTRIP data from "www.rtk2go.com". The code written in python works well and serves the purpose.
import socket
import base64
server = "www.rtk2go.com"
port = "2101"
mountpoint = "leedgps"
username = ""
password = ""
def getHTTPBasicAuthString(username, password):
inputstring = username + ':' + password
pwd_bytes = base64.standard_b64encode(inputstring.encode("utf-8"))
pwd = pwd_bytes.decode("utf-8").replace('\n', '')
return pwd
pwd = getHTTPBasicAuthString(username, password)
print(pwd)
header = "GET /{} HTTP/1.0\r\n".format(mountpoint) + \
"User-Agent: NTRIP u-blox\r\n" + \
"Accept: */*\r\n" + \
"Authorization: Basic {}\r\n".format(pwd) + \
"Connection: close\r\n\r\n"
print(header)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, int(port)))
s.sendto(header.encode('utf-8'), (server, int(port)))
resp = s.recv(1024)
try:
while True:
try:
data = s.recv(2048)
except:
pass
finally:
s.close()
I wanted to implement the same thing in c++ code and after going through few online tutorials, I wrote the following code in C++ (I am very new to C++)
#include <iostream>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
using namespace std;
#define SIZE 1000
#define PORT 2101
string base64Encoder(string input_str, int len_str) {
char char_set[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *res_str = (char *) malloc(SIZE * sizeof(char));
int index, no_of_bits = 0, padding = 0, val = 0, count = 0, temp;
int i, j, k = 0;
for (i = 0; i < len_str; i += 3) {
val = 0, count = 0, no_of_bits = 0;
for (j = i; j < len_str && j <= i + 2; j++) {
val = val << 8;
val = val | input_str[j];
count++;
}
no_of_bits = count * 8;
padding = no_of_bits % 3;
while (no_of_bits != 0) {
// retrieve the value of each block
if (no_of_bits >= 6) {
temp = no_of_bits - 6;
// binary of 63 is (111111) f
index = (val >> temp) & 63;
no_of_bits -= 6;
} else {
temp = 6 - no_of_bits;
// append zeros to right if bits are less than 6
index = (val << temp) & 63;
no_of_bits = 0;
}
res_str[k++] = char_set[index];
}
}
for (i = 1; i <= padding; i++) {
res_str[k++] = '=';
}
res_str[k] = '\0';
string a = res_str;
return a;
}
int main() {
string input_str = ":";
int len_str;
len_str = input_str.length();
string pwd = base64Encoder(input_str, len_str);
string mountpoint = "leedgps";
string header = "GET /" + mountpoint + " HTTP/1.0\r\n" + \
"User-Agent: NTRIP u-blox\r\n" + \
"Accept: */*\r\n" + \
"Authorization: Basic " + pwd + "\r\n" + \
"Connection: close\r\n\r\n";
struct hostent *h;
if ((h = gethostbyname("www.rtk2go.com")) == NULL) { // Lookup the hostname
cout << "cannot look up hostname" << endl;
}
struct sockaddr_in saddr;
int sockfd, connfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0;
if (sockfd) {
printf("Error creating socket\n");
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(2101);
if(inet_pton(AF_INET, "3.23.52.207", &saddr.sin_addr)<=0) //
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
cout << connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) << endl;
return 0;
}
But the connect method always returns -1 (in C++). Any idea what am I doing wrong?
sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0;
means that sockfd is either 0 or 1, which is not a valid socket.
Do this instead:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
printf("Error creating socket\n");
}
I am trying to get my firebase data and display it to my 7-segment using Arduino and I am getting the help of a python script to send data to the serial monitor. While it detects the Serial.available() inside the loop but I don't know how could I get that particular value and display its first digit to my 7-segment
Currently for the test I am just trying to display 1 if there is data matches inside serial monitor and arduino
Adruino:
#define A PA0
#define B PA1
#define C PA2
#define D PA3
#define E PA4
#define F PA5
#define G PA6
int a = 2;
void setup() {
Serial.begin(9600);
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
}
void loop() {
if(Serial.available()){
// Serial.println("0 1 2 3 4 5 6 7 8 9");
// sevenSeg(1, 1, 1, 1, 1, 1, 0); // 0
// delay(500);
if(Serial.readString() == Serial.readString()){
sevenSeg(0, 0, 0, 1, 1, 0, 0); // 1
delay(500);
}
}else{
sevenSeg(1, 1, 1, 1, 1, 1, 0);
}// 0
}
void sevenSeg (int g, int f, int e, int d, int c, int b, int a)
{
digitalWrite(A, a);
digitalWrite(B, b);
digitalWrite(C, c);
digitalWrite(D, d);
digitalWrite(E, e);
digitalWrite(F, f);
digitalWrite(G, g);
}
Python Script:
import pyrebase
import argparse
import datetime
import time
import math
import serial
from serial import Serial
time_str = datetime.datetime.now().strftime("%Y%b%d_%I:%M:%S%p").upper()
ap = argparse.ArgumentParser()
ap.add_argument("-t", "--temp", help="Patient temperature at time of reading") #
ap.add_argument("-r", "--heart", help="Patient heart rate at the time of reading")
args = vars(ap.parse_args())
#firebase configuration
firebaseConfig = {
"apiKey": "",
"authDomain": "heartrate-firebase.firebaseapp.com",
"databaseURL": "https://heartrate-firebase-default-rtdb.firebaseio.com/",
"projectId": "heartrate-firebase",
"storageBucket": "heartrate-firebase.appspot.com",
"messagingSenderId": "851991577466",
"appId": "1:851991577466:web:daaf323d9af64fd3318cc3",
}
firebase = pyrebase.initialize_app(firebaseConfig)
db = firebase.database()
if args.get("temp", None) is not None:
data = {"Temperature": args.get("temp", None)}
db.child("Temperature").child(time_str).set(data)
if args.get("heart", None) is not None:
data = {"HeartRate": args.get("heart", None)}
db.child("HeartRate").child(time_str).set(data)
user = db.child("HeartRate").get()
for x in user.each():
date = db.child("HeartRate").child(x.key()).child("HeartRate").get()
print("Heart Rate: " + date.val())
userTemp = db.child("Temperature").get()
for x in userTemp.each():
date = db.child("Temperature").child(x.key()).child("Temperature").get()
# date = db.child("Temperature").get()
print("Temperature: " + date.val())
with serial.Serial('COM4', 9600, timeout=5) as ser:
time.sleep(2)
ser.write((date.val() + '\n').encode())
time.sleep(1)
y = ser.read(ser.inWaiting())
print(y)
# ser.close()
All I end up with is displaying 0. Any help or ideas are greatly appreciated. Thanks.
the problem was I was receiving 2\r\n from the database so to remove \r\n I used strip() to remove \r\n and it worked.
I am trying to make a resource monitor with an arduino and it is working great for almost 20 seconds before it almost stops running.
When it slows down, it takes almost 5 seconds between updates.
I have tried to comment out everything with psutil and giving it a permanent value.
and have tried the same with GPUtil.
Here is the python code
import serial.tools.list_ports
import serial
import psutil
import GPUtil
import time
import serial
ports = list(serial.tools.list_ports.comports())
baud = 9600
for p in ports:
if "Arduino" in p[1]:
port = p[0]
ser=serial.Serial(port, baud, timeout=1)
try:
while True:
cpuUsage = psutil.cpu_percent()
ramUsage = psutil.virtual_memory()
cpuUsage = str(cpuUsage)
GPUs = GPUtil.getGPUs()
gpuUsage = GPUs[0].load
gpuUsage = str(gpuUsage)
gpuUsage = gpuUsage[2:]
ramUsage = str(ramUsage.percent)
toSend = cpuUsage + "," + gpuUsage + ","+ ramUsage
print (toSend)
ser.write(toSend.encode())
#print("20.5".encode())
#line = ser.readline()[:-2]
#line.decode()
#print ("Read : " , line);
time.sleep(0.1)
except:
print ("error")
Here is the Arduino code
#include <FastLED.h>
#define NUM_LEDS 15
#define DATA_PIN 6
float Cpu = 40;
float Gpu = 99;
float Ram = 60;
String Cpu_Read;
String Gpu_Read;
String Ram_Read;
int RXLED = 17;
String StrNumbers = "";
int numbers;
int Received = 0;
int Separrator = 0;
String Text = "";
CRGB leds[NUM_LEDS];
void setup() {
// put your setup code here, to run once:
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(5);
Serial.begin(115200);
pinMode(LED_BUILTIN_TX,INPUT);
pinMode(LED_BUILTIN_RX,INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
char inByte = ' ';
StrNumbers = "";
Text = "";
Separrator = 0;
Cpu_Read = "";
Gpu_Read = "";
Ram_Read = "";
Received = 0;
pinMode(LED_BUILTIN_TX,INPUT);
while(Serial.available() > 0){ // only send data back if data has been sent
pinMode(LED_BUILTIN_TX,OUTPUT);
inByte = Serial.read(); // read the incoming data
if (inByte == ','){
Separrator += 1;
}
else if (Separrator == 0){
Cpu_Read += (char) inByte;
}
else if (Separrator == 1){
Gpu_Read += (char) inByte;
}
else if (Separrator == 2){
Ram_Read += (char) inByte;
Serial.print("Ram : ");
Serial.println(Ram_Read);
}
else{
Serial.println("Error");
}
/*
inByte = Serial.read(); // read the incoming data
if (isDigit(inByte)) { // tests if inByte is a digit
StrNumbers += (char) inByte;
Serial.println(inByte); // send the data back in a new line so that it is not all one long line
//Serial.println(numbers); // send the data back in a new line so that it is not all one long line
}
else if (inByte == ".")
{
abortLoop = 1;
}
else{
Text +=(char) inByte;
}
*/
Received = 1;
}
if (Received == 1){
Cpu = Cpu_Read.toInt();
Gpu = Gpu_Read.toInt();
Ram = Ram_Read.toInt();
UpdateMonitor(Cpu ,Gpu ,Ram);
}
Text.trim();
if (StrNumbers != ""){
numbers = StrNumbers.toInt();
Serial.println(numbers);
if (numbers > 100) numbers = 100;
UpdateMonitor(Cpu,numbers,Ram);
}
if(Text!= ""){
//Serial.println(Text); // send the data back in a new line so that it is not all one long line
}
if (Text == "ResourceMonitor"){
Serial.println("Yes");
}
else if (Text != ""){
Serial.println(Text);
numbers = Text.toInt();
if (numbers > 100) numbers = 100;
UpdateMonitor(Cpu, numbers, Ram);
}
}
void UpdateMonitor(int cpu,int gpu, int ram){
int Cpu_Usage = map(cpu, 0, 100, 0, 5);
int Gpu_Usage = map(gpu, 0, 100, 0, 5);
int Ram_Usage = map(ram, 0, 100, 0, 5);
FastLED.clear();
for(int led = 0; led < Ram_Usage; led++) {
leds[led] = CRGB::Blue;
}
for(int led = 0; led < Gpu_Usage; led++) {
leds[9 - led] = CRGB::Green;
}
for(int led = 0; led < Cpu_Usage; led++) {
leds[led+10] = CRGB::Red;
}
FastLED.show();
}
it is fixed now not sure what fixet it but it works atleast :)
thansk to all that have tried to helt
I would like to trigger data acquisition on my Arduino UNO with connected accelerometer (MPU 6050) by using Python.
The idea is that when the command is given in Python, the Arduino would start saving data on its SRAM and when a certain number of measurements would be saved, the data would be sent in a package back to Python.
This is my current Arduino code:
#include<Wire.h>
#define MPU6050_DLPF_94HZ MPU6050_DLPF_CFG_2
const int MPU_addr_1 = 0x68; // I2C address of the first MPU-6050
const int baudrate = 19200;
int16_t AcX1; // definition of variables
const int len = 200; // Buffer size
float analogDataArray[len];
int count = 0;
void setup() {
Wire.begin();
Wire.beginTransmission(MPU_addr_1);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
Serial.begin(baudrate);
}
void loop() {
Wire.beginTransmission(MPU_addr_1);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr_1, 8, true); // request a total of 14 registers
float AcX1 = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
if (Serial.available())
{
if (Serial.read() == 'Y') {
analogDataArray[count] = AcX1;
count = count + 1;
if (count >= len) {
for (int i; i <= len; i = i + 1) {
Serial.println(analogDataArray[i] / 16384);
}
}
count = 0;
}
}
delay(5);
}
and this is my Python code:
import serial
arduinoData = serial.Serial('COM3', 19200)
com = input('Press "Y":' )
arduinoData.write(bytes(com, 'utf-8'))
vec = []
run = True
while run is True:
while (arduinoData.inWaiting() == 0):
pass
arduinoString = arduinoData.readline()
vec.append(float(arduinoString))
if len(vec) >= 100:
run = False
print(vec)
I've managed to get it working for 1 measurement but as soon as I defined an array inside Arduino to save multiple measurements, the code doesn't work. I'm sure that it is close to working, but I can't find the detail that is stopping me from that.
Thank you for any provided help.
Kind regards,
L
I got it working, the problem was in my Arduino code, as expected.
#include<Wire.h>
#define MPU6050_DLPF_94HZ MPU6050_DLPF_CFG_2
const int MPU_addr_1 = 0x68; // I2C address of the first MPU-6050
const int baudrate = 9600;
int16_t AcX1; // definition of variables
const int len = 200; // Buffer size
float analogDataArray[len];
int count = 0;
int ans;
void setup() {
Wire.begin();
Wire.beginTransmission(MPU_addr_1);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
Serial.begin(baudrate);
}
void loop() {
Wire.beginTransmission(MPU_addr_1);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr_1, 8, true); // request a total of 14 registers
float AcX1 = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
if (Serial.available() && Serial.read() == 'Y') {
ans = 1;
}
if (ans == 1) {
analogDataArray[count] = AcX1;
count = count + 1;
if (count >= len) {
for (int i; i <= len; i = i + 1) {
Serial.println(analogDataArray[i] / 16384);
}
ans = 0;
}
}
delay(5);
}
I need to send an array (representing an image) through a named FIFO pipe from a python process to a c++ process, and then back the other way (on a Linux system).
The below code works great when using named pipes between two Python processes. It uses numpy's tostring() and fromstring() functions:
Send frames over named pipe (Python)
import cv2
import numpy as np
from time import sleep
##########################################################
FIFO_Images = "./../pipes/images.fifo"
videoName = "./../../videos/videoName.avi"
delim = "break"
##########################################################
def sendImage(h, w, d, pixelarray):
imageString = pixelarray.tostring()
with open(FIFO_Images, "w") as f:
f.write(str(h)+ delim + str(w)+ delim + str(d) + delim + imageString)
sleep(.01)
return
##########################################################
cap = cv2.VideoCapture(videoName)
while(cap.isOpened()):
ret, frame_rgb = cap.read()
h, w, d = frame_rgb.shape
sendImage(h, w, d, frame_rgb)
cap.release()
cv2.destroyAllWindows()
Read frames over named pipe (Python)
import cv2
import numpy as np
##########################################################
FIFO_Images = "./../pipes/images.fifo"
delim = "break"
##########################################################
def getFrame():
with open(FIFO_Images, "r") as f:
data = f.read().split(delim)
#parse incoming string, which has format (height, width, depth, imageData)
h=int(data[0])
w=int(data[1])
d=int(data[2])
imageString = data[3]
#convert array string into numpy array
array = np.fromstring(imageString, dtype=np.uint8)
#reshape numpy array into the required dimensions
frame = array.reshape((h,w,d))
return frame
##########################################################
while(True):
frame = getFrame()
cv2.imshow('frame', frame)
cv2.waitKey(1) & 0xFF
However, I couldn't figure out how to read the entire image from the pipe on the cpp side, since it takes "\n" as a delimiter for the read automatically.
My workaround was to do a base64 encoding on the "tostring()" image, then send that over the pipe. This works, but the base64 decoding on the other slide is much too slow for real-time applications (~0.2 seconds per frame). Code:
Send base64-encoded images over named pipe (Python)
import cv2
import numpy as np
from time import time
from time import sleep
import base64
##########################################################
FIFO_Images = "./../pipes/images.fifo"
videoName = "./../../videos/videoName.avi"
delim = ";;"
##########################################################
def sendImage(h, w, d, pixelarray):
flat = pixelarray.flatten()
imageString = base64.b64encode(pixelarray.tostring())
fullString = str(h)+ delim + str(w)+ delim + str(d)+ delim + imageString + delim + "\n"
with open(FIFO_Images, "w") as f:
f.write(fullString)
return
##########################################################
cap = cv2.VideoCapture(videoName)
count = 0
while(cap.isOpened()):
ret, frame_rgb = cap.read()
h, w, d = frame_rgb.shape
frame_gbr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)
sendImage(h, w, d, frame_rgb)
cap.release()
cv2.destroyAllWindows()
Read base64-encoded images over named pipe (C++)
#include "opencv2/opencv.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <linux/stat.h>
#include <ctime>
using namespace std;
using namespace cv;
#define FIFO_FILE "./../../../pipes/images.fifo"
#define MAX_BUF 10000000
FILE *fp;
char readbuf[MAX_BUF + 1]; //add 1 to the expected size to accomodate the mysterious "extra byte", which I think signals the end of the line.
/************************BASE64 Decoding*********************************************/
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
/*********************************************************************/
int stringToInt(string str)
{
int num;
if (!(istringstream(str) >> num)) num = 0;
return num;
}
/*********************************************************************/
bool timerOn = 0;
clock_t timerStart;
void Timer(string process)
{
if (!timerOn)
{
timerStart = clock();
timerOn = true;
}
else if (timerOn)
{
double duration = (clock() - timerStart) / (double) CLOCKS_PER_SEC;
cout << "Time to complete: ";
printf("%.2f", duration);
cout << ": " << process << endl;
timerOn = false;
}
}
/*********************************************************************/
void getFrame()
{
string fullString;
string delimiter = ";;";
size_t pos = 0;
string token;
int h;
int w;
int d;
string imgString;
int fifo;
bool cont(true);
/***************************
Read from the pipe
www.tldp.org/LDP/lpg/node18.html
***************************/
Timer("Read from pipe");
fp = fopen(FIFO_FILE, "r");
fgets(readbuf, MAX_BUF + 1, fp); // Stops when MAX_BUF characters are read, the newline character ("\n") is read, or the EOF (end of file) is reached
string line(readbuf);
fclose(fp);
Timer("Read from pipe");
//////parse the string into components
Timer("Parse string");
int counter = 0;
while ((pos = line.find(delimiter)) != string::npos)
{
token = line.substr(0,pos);
if (counter == 0)
{
h = stringToInt(token);
}
else if (counter == 1)
{
w = stringToInt(token);
}
else if (counter == 2)
{
d = stringToInt(token);
}
else if (counter == 3)
{
imgString = token;
//cout << imgString[0] << endl;
}
else
{
cout << "ERROR: Too many paramaters passed" << endl;
return;
}
line.erase(0, pos + delimiter.length());
counter ++;
}
if (counter == 3)
{
imgString = token;
}
if (counter < 3)
{
cout << "ERROR: Not enough paramaters passed: " << counter << endl;
//return;
}
Timer("Parse string");
/***************************
Convert from Base64
***************************/
Timer("Decode Base64");
std::string decoded = base64_decode(imgString);
Timer("Decode Base64");
/***************************
Convert to vector of ints
***************************/
Timer("Convert to vector of ints");
std::vector<uchar> imgVector;
for (int i = 0; i < decoded.length(); i = i+1) // + 4)
{
int temp = (char(decoded[i]));
imgVector.push_back(temp);
}
Timer("Convert to vector of ints");
//////convert the vector into a matrix
Mat frame = Mat(imgVector).reshape(d, h);
namedWindow("Frame", WINDOW_AUTOSIZE);
imshow("Frame", frame);
waitKey(1);
}
int main()
{
/* Create the FIFO if it does not exist */
umask(0);
mknod(FIFO_FILE, S_IFIFO|0666, 0);
while(1)
{
getFrame();
}
return 0;
}
There must be a more efficient way to accomplish this. Can anyone make a recommendation? While I'm happy to hear suggestions for other methods to accomplish this, I am constrained to using named pipes for now.
This is overcomplicated. If you need to send binary data, send their length first, then newline (\n), and then the data (raw, no base64). Receive it on the other side by readling a line, parsing the number and then just reading a block of data of given length.
Example - writing binary data to a FIFO (or file) in Python:
#!/usr/bin/env python3
import os
fifo_name = 'fifo'
def main():
data = b'blob\n\x00 123'
try:
os.mkfifo(fifo_name)
except FileExistsError:
pass
with open(fifo_name, 'wb') as f:
# b for binary mode
f.write('{}\n'.format(len(data)).encode())
f.write(data)
if __name__ == '__main__':
main()
Reading binary data from FIFO in C++:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
const char *fifo_name = "fifo";
mknod(fifo_name, S_IFIFO | 0666, 0);
std::ifstream f(fifo_name);
std::string line;
getline(f, line);
auto data_size = std::stoi(line);
std::cout << "Size: " << data_size << std::endl;
std::string data;
{
std::vector<char> buf(data_size);
f.read(buf.data(), data_size);
// write to vector data is valid since C++11
data.assign(buf.data(), buf.size());
}
if (!f.good()) {
std::cerr << "Read failed" << std::endl;
}
std::cout << "Data size: " << data.size() << " content: " << data << std::endl;
}